From 39a991b807d1b40ce8e3d88b1fcb9ec8e2b0b2e2 Mon Sep 17 00:00:00 2001 From: Vilsol Date: Mon, 8 Oct 2018 22:49:32 +0300 Subject: [PATCH 01/10] Meta Block, Makefiles, Compile Fix --- .gitignore | 3 +- Makefile | 12 ++++++++ resource/resource.go | 2 +- schema/schema.go | 70 +++++++++++++++++++++----------------------- 4 files changed, 48 insertions(+), 39 deletions(-) create mode 100644 Makefile diff --git a/.gitignore b/.gitignore index c597668..9aeb577 100644 --- a/.gitignore +++ b/.gitignore @@ -171,4 +171,5 @@ $RECYCLE.BIN/ # End of https://www.gitignore.io/api/go,osx,linux,windows,jetbrains+all -config.json \ No newline at end of file +config.json +/core \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9fe3638 --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +GOCMD=go +GOBUILD=$(GOCMD) build +BINARY_NAME=core + +all: build + +build: + $(GOBUILD) -o $(BINARY_NAME) cmd/cli/main.go + +run: + $(MAKE) build + ./$(BINARY_NAME) \ No newline at end of file diff --git a/resource/resource.go b/resource/resource.go index 7d0016b..5680ff2 100644 --- a/resource/resource.go +++ b/resource/resource.go @@ -6,7 +6,7 @@ import ( ) func GetStore(resource string) *plugins.Storage { - return plugins.GetStore(schema.GetSchema(resource).GetStore()) + return plugins.GetStore(schema.GetSchema(resource).Meta.Store) } func GetResources() []string { diff --git a/schema/schema.go b/schema/schema.go index 200ce30..edeba99 100644 --- a/schema/schema.go +++ b/schema/schema.go @@ -14,6 +14,12 @@ import ( type Schema struct { Data gojsonschema.JSONLoader Source map[string]interface{} + Meta ResourceMeta +} + +type ResourceMeta struct { + Resource string `json:"resource"` + Store string `json:"store"` } var schemas = make(map[string]Schema) @@ -29,22 +35,44 @@ func InitializeSchemas() { if strings.HasSuffix(f.Name(), ".json") { bytes, _ := ioutil.ReadFile("./resources/" + f.Name()) schema := gojsonschema.NewBytesLoader(bytes) - var source interface{} - json.Unmarshal(bytes, &source) + var source map[string]interface{} + err := json.Unmarshal(bytes, &source) + + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to load resource %s:\n", f.Name()) + fmt.Fprintln(os.Stderr, err) + continue + } + + if _, ok := source["meta"]; !ok { + fmt.Fprintf(os.Stderr, "Failed to load resource %s:\n", f.Name()) + fmt.Fprintln(os.Stderr, "resource does not contain meta object") + continue + } + + temp, _ := json.Marshal(source["meta"]) + meta := new(ResourceMeta) + err = json.Unmarshal(temp, &meta) + + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to load resource %s:\n", f.Name()) + fmt.Fprintln(os.Stderr, err) + continue + } s := Schema{ Data: schema, - Source: source.(map[string]interface{}), + Source: source, + Meta: *meta, } - resource, err := s.GetResource() if err != nil { fmt.Fprintf(os.Stderr, "Failed to load resource %s:\n", f.Name()) fmt.Fprintln(os.Stderr, err) continue } - schemas[resource] = s + schemas[meta.Resource] = s } } @@ -88,35 +116,3 @@ func GetSchema(resource string) *Schema { schema := schemas[resource] return &schema } - -func (schema Schema) GetRaw(key string) (interface{}, error) { - if _, ok := schema.Source[key]; !ok { - return nil, errors.New("key '" + key + "' does not exist in schema") - } - - return schema.Source[key], nil -} - -func (schema Schema) GetRawString(key string) (string, error) { - raw, err := schema.GetRaw(key) - - if err != nil { - return "", err - } - - casted, ok := raw.(string) - - if !ok { - return "", errors.New("key '" + key + "' is not of type string in schema") - } - - return casted, nil -} - -func (schema Schema) GetStore() (string, error) { - return schema.GetRawString("store") -} - -func (schema Schema) GetResource() (string, error) { - return schema.GetRawString("resource") -} From 49ffd668e82902f9cfef709789bca913f69bfbfd Mon Sep 17 00:00:00 2001 From: Vilsol Date: Sat, 13 Oct 2018 06:48:30 +0300 Subject: [PATCH 02/10] Create an interface between core and plugins --- api.go | 20 +++--- go.mod | 6 +- go.sum | 9 --- plugins/manager.go | 87 -------------------------- plugins/registry.go | 132 --------------------------------------- plugins/types.go | 54 ---------------- registry/manager.go | 123 ++++++++++++++++++++++++++++++++++++ registry/registry.go | 145 +++++++++++++++++++++++++++++++++++++++++++ resource/resource.go | 22 +++++-- schema/schema.go | 56 ++++++++++------- 10 files changed, 331 insertions(+), 323 deletions(-) delete mode 100644 plugins/manager.go delete mode 100644 plugins/registry.go delete mode 100644 plugins/types.go create mode 100644 registry/manager.go create mode 100644 registry/registry.go diff --git a/api.go b/api.go index f4d6ef3..fd25d44 100644 --- a/api.go +++ b/api.go @@ -2,7 +2,8 @@ package Core import ( "github.com/ResourceAPI/Core/config" - "github.com/ResourceAPI/Core/plugins" + "github.com/ResourceAPI/Core/registry" + "github.com/ResourceAPI/Core/resource" "github.com/ResourceAPI/Core/schema" ) @@ -10,24 +11,25 @@ func Run() { // Initialize Core config.InitializeConfig() schema.InitializeSchemas() + resource.InitializeResources() // Initialize Plugins - plugins.InitializePlugins() - plugins.InitializeStores() - plugins.InitializeFilters() - plugins.InitializeFacades() + registry.InitializePlugins() + registry.InitializeStores() + registry.InitializeFilters() + registry.InitializeFacades() // Start up stores - plugins.StartStores() + registry.StartStores() // Start up filters - plugins.StartFilters() + registry.StartFilters() // Start up facades - plugins.StartFacades() + registry.StartFacades() // Wait for goroutines - plugins.WaitForGoroutines() + registry.WaitForGoroutines() // TODO Graceful shutdown } diff --git a/go.mod b/go.mod index c34f7ba..82a13db 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,10 @@ module github.com/ResourceAPI/Core +replace github.com/ResourceAPI/Interface v0.0.0 => ../Interface + require ( - github.com/Vilsol/GoLib v0.0.7 + github.com/ResourceAPI/Interface v0.0.0 github.com/davecgh/go-spew v1.1.1 // indirect - github.com/gorilla/handlers v1.4.0 - github.com/gorilla/mux v1.6.2 github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/testify v1.2.2 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect diff --git a/go.sum b/go.sum index b724fac..f9bfe51 100644 --- a/go.sum +++ b/go.sum @@ -1,14 +1,5 @@ -github.com/Vilsol/GoLib v0.0.7 h1:zG0skAT/4qPwA/ohDR2KJ90SR8JAfbtZSkq/Lox2oPE= -github.com/Vilsol/GoLib v0.0.7/go.mod h1:6c39n5wTtt5OSt8vEF1mlnc/lC+Xl0SlHG3vboC2pEQ= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f h1:9oNbS1z4rVpbnkHBdPZU4jo9bSmrLpII768arSyMFgk= -github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/handlers v1.4.0 h1:XulKRWSQK5uChr4pEgSE4Tc/OcmnU9GJuSwdog/tZsA= -github.com/gorilla/handlers v1.4.0/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= -github.com/gorilla/mux v1.6.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk= -github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= diff --git a/plugins/manager.go b/plugins/manager.go deleted file mode 100644 index bf05c03..0000000 --- a/plugins/manager.go +++ /dev/null @@ -1,87 +0,0 @@ -package plugins - -import ( - "sync" -) - -var storageWaitGroup sync.WaitGroup -var facadeWaitGroup sync.WaitGroup -var filtersWaitGroup sync.WaitGroup - -func InitializeStores() { - for _, store := range stores { - (*store).Initialize() - } -} - -func StartStores() { - storageWaitGroup.Add(len(stores)) - - for _, store := range stores { - go func() { - defer storageWaitGroup.Done() - (*store).Start() - }() - } -} - -func StopStores() { - // TODO 30s timeout - for _, store := range stores { - (*store).Stop() - } -} - -func InitializeFacades() { - for _, facade := range facades { - (*facade).Initialize() - } -} - -func StartFacades() { - facadeWaitGroup.Add(len(facades)) - - for _, facade := range facades { - go func() { - defer facadeWaitGroup.Done() - (*facade).Start() - }() - } -} - -func StopFacades() { - // TODO 30s timeout - for _, facade := range facades { - (*facade).Stop() - } -} - -func InitializeFilters() { - for _, filter := range filters { - (*filter).Initialize() - } -} - -func StartFilters() { - filtersWaitGroup.Add(len(facades)) - - for _, filter := range filters { - go func() { - defer filtersWaitGroup.Done() - (*filter).Start() - }() - } -} - -func StopFilters() { - // TODO 30s timeout - for _, filter := range filters { - (*filter).Stop() - } -} - -func WaitForGoroutines() { - facadeWaitGroup.Wait() - storageWaitGroup.Wait() - filtersWaitGroup.Wait() -} diff --git a/plugins/registry.go b/plugins/registry.go deleted file mode 100644 index 2bc252f..0000000 --- a/plugins/registry.go +++ /dev/null @@ -1,132 +0,0 @@ -package plugins - -import ( - "fmt" - "io/ioutil" - "plugin" - - "github.com/ResourceAPI/Core/config" -) - -var facades map[string]*Facade -var stores map[string]*Storage -var filters map[string]*Filter -var associates map[string][]string - -func InitializePlugins() { - facades = make(map[string]*Facade) - stores = make(map[string]*Storage) - filters = make(map[string]*Filter) - associates = make(map[string][]string) - - files, err := ioutil.ReadDir(config.Get().PluginDirectory) - - if err != nil { - panic(err) - } - - loadedPlugins := make([]Plugin, 0) - for _, f := range files { - plug, err := plugin.Open(config.Get().PluginDirectory + "/" + f.Name()) - if err != nil { - fmt.Println(err) - continue - } - - entrypoint, err := plug.Lookup("CorePlugin") - if err != nil { - fmt.Println(err) - continue - } - - var pl Plugin - pl, ok := entrypoint.(Plugin) - if !ok { - fmt.Println("unexpected type from module symbol") - continue - } - - pl.Entrypoint() - loadedPlugins = append(loadedPlugins, pl) - } - - pluginNames := make([]string, 0) - facadeNames := make([]string, 0) - storageNames := make([]string, 0) - filterNames := make([]string, 0) - - for _, v := range loadedPlugins { - pluginNames = append(pluginNames, v.Name()) - } - - for k := range facades { - facadeNames = append(facadeNames, k) - } - - for k := range stores { - storageNames = append(storageNames, k) - } - - for k := range filters { - filterNames = append(filterNames, k) - } - - fmt.Printf("Loaded %d plugin(s): %+v\n", len(loadedPlugins), pluginNames) - fmt.Printf("Loaded %d facade(s): %+v\n", len(facades), facadeNames) - fmt.Printf("Loaded %d storage(s): %+v\n", len(stores), storageNames) - fmt.Printf("Loaded %d filter(s): %+v\n", len(filters), filterNames) - fmt.Printf("Loaded %d filter association(s)\n", len(associates)) -} - -func RegisterFacade(name string, facade Facade) error { - if _, ok := facades[name]; ok { - panic("Facade with name " + name + " is already registered!") - } - - facades[name] = &facade - - return nil -} - -func RegisterStorage(name string, storage Storage) error { - if _, ok := stores[name]; ok { - panic("Storage with name " + name + " is already registered!") - } - - stores[name] = &storage - - return nil -} - -func RegisterFilter(name string, filter Filter) error { - if _, ok := filters[name]; ok { - panic("Filter with name " + name + " is already registered!") - } - - filters[name] = &filter - - return nil -} - -func AssociateFilter(filter string, storage string) error { - if _, ok := associates[filter]; !ok { - associates[filter] = make([]string, 0) - } - - supportedStores := associates[filter] - - for _, store := range supportedStores { - if store == storage { - panic("Filter " + filter + " is already associated with storage " + storage + "!") - } - } - - supportedStores = append(supportedStores, storage) - associates[filter] = supportedStores - - return nil -} - -func GetStore(store string) *Storage { - return stores[store] -} diff --git a/plugins/types.go b/plugins/types.go deleted file mode 100644 index ec81a57..0000000 --- a/plugins/types.go +++ /dev/null @@ -1,54 +0,0 @@ -package plugins - -type Plugin interface { - Name() string - Entrypoint() -} - -type Facade interface { - // Initialize the facade. - Initialize() error - - // Start the facade. Must be a blocking call. - Start() error - - // Graceful stopping of the facade with a 30s timeout. - Stop() error -} - -type Storage interface { - // Initialize the storage. - Initialize() error - - // Start the storage. Must be a blocking call. - Start() error - - // Graceful stopping of the storage with a 30s timeout. - Stop() error - - // Retrieve resources. - GetResources(resource string, filters []interface{}) ([]map[string]interface{}, error) - - // Create resources. - CreateResources(resource string, data []map[string]interface{}) error - - // Update resources. - UpdateResources(resource string, data []map[string]interface{}, filters []interface{}) error - - // Delete resources. - DeleteResources(resource string, filters []interface{}) error -} - -type Filter interface { - // Initialize the filter. - Initialize() error - - // Start the filter. Does not have to be blocking. - Start() error - - // Graceful stopping of the filter with a 30s timeout. - Stop() error - - // Validate structure for filter validness - ValidateFilter(filter interface{}) (bool, error) -} diff --git a/registry/manager.go b/registry/manager.go new file mode 100644 index 0000000..520e84f --- /dev/null +++ b/registry/manager.go @@ -0,0 +1,123 @@ +package registry + +import ( + "sync" +) + +var storageWaitGroup sync.WaitGroup +var facadeWaitGroup sync.WaitGroup +var filtersWaitGroup sync.WaitGroup + +func InitializeStores() { + for _, store := range coreRegistry.stores { + err := (*store).Initialize() + + if err != nil { + panic(err) + } + } +} + +func StartStores() { + storageWaitGroup.Add(len(coreRegistry.stores)) + + for _, store := range coreRegistry.stores { + go func() { + defer storageWaitGroup.Done() + err := (*store).Start() + + if err != nil { + panic(err) + } + }() + } +} + +func StopStores() { + // TODO 30s timeout + for _, store := range coreRegistry.stores { + err := (*store).Stop() + + if err != nil { + panic(err) + } + } +} + +func InitializeFacades() { + for _, facade := range coreRegistry.facades { + err := (*facade).Initialize() + + if err != nil { + panic(err) + } + } +} + +func StartFacades() { + facadeWaitGroup.Add(len(coreRegistry.facades)) + + for _, facade := range coreRegistry.facades { + go func() { + defer facadeWaitGroup.Done() + err := (*facade).Start() + + if err != nil { + panic(err) + } + }() + } +} + +func StopFacades() { + // TODO 30s timeout + for _, facade := range coreRegistry.facades { + err := (*facade).Stop() + + if err != nil { + panic(err) + } + } +} + +func InitializeFilters() { + for _, filter := range coreRegistry.filters { + err := (*filter).Initialize() + + if err != nil { + panic(err) + } + } +} + +func StartFilters() { + filtersWaitGroup.Add(len(coreRegistry.facades)) + + for _, filter := range coreRegistry.filters { + go func() { + defer filtersWaitGroup.Done() + err := (*filter).Start() + + if err != nil { + panic(err) + } + }() + } +} + +func StopFilters() { + // TODO 30s timeout + for _, filter := range coreRegistry.filters { + err := (*filter).Stop() + + if err != nil { + panic(err) + } + } +} + +func WaitForGoroutines() { + facadeWaitGroup.Wait() + storageWaitGroup.Wait() + filtersWaitGroup.Wait() +} diff --git a/registry/registry.go b/registry/registry.go new file mode 100644 index 0000000..93a2b8a --- /dev/null +++ b/registry/registry.go @@ -0,0 +1,145 @@ +package registry + +import ( + "fmt" + "io/ioutil" + "plugin" + + "github.com/ResourceAPI/Core/config" + "github.com/ResourceAPI/Interface/plugins" +) + +type CoreRegistry struct { + facades map[string]*plugins.Facade + stores map[string]*plugins.Storage + filters map[string]*plugins.Filter + associates map[string][]string +} + +var coreRegistry *CoreRegistry + +func GetRegistryInternal() *CoreRegistry { + return coreRegistry +} + +func InitializePlugins() { + coreRegistry = &CoreRegistry{ + facades: make(map[string]*plugins.Facade), + stores: make(map[string]*plugins.Storage), + filters: make(map[string]*plugins.Filter), + associates: make(map[string][]string), + } + + files, err := ioutil.ReadDir(config.Get().PluginDirectory) + + if err != nil { + panic(err) + } + + plugins.SetRegistry(coreRegistry) + + loadedPlugins := make([]plugins.Plugin, 0) + for _, f := range files { + plug, err := plugin.Open(config.Get().PluginDirectory + "/" + f.Name()) + if err != nil { + fmt.Println(err) + continue + } + + entrypoint, err := plug.Lookup("CorePlugin") + if err != nil { + fmt.Println(err) + continue + } + + var pl plugins.Plugin + pl, ok := entrypoint.(plugins.Plugin) + if !ok { + fmt.Println("unexpected type from module symbol") + continue + } + + pl.Entrypoint() + loadedPlugins = append(loadedPlugins, pl) + } + + pluginNames := make([]string, 0) + facadeNames := make([]string, 0) + storageNames := make([]string, 0) + filterNames := make([]string, 0) + + for _, v := range loadedPlugins { + pluginNames = append(pluginNames, v.Name()) + } + + for k := range coreRegistry.facades { + facadeNames = append(facadeNames, k) + } + + for k := range coreRegistry.stores { + storageNames = append(storageNames, k) + } + + for k := range coreRegistry.filters { + filterNames = append(filterNames, k) + } + + fmt.Printf("Loaded %d plugin(s): %+v\n", len(loadedPlugins), pluginNames) + fmt.Printf("Loaded %d facade(s): %+v\n", len(coreRegistry.facades), facadeNames) + fmt.Printf("Loaded %d storage(s): %+v\n", len(coreRegistry.stores), storageNames) + fmt.Printf("Loaded %d filter(s): %+v\n", len(coreRegistry.filters), filterNames) + fmt.Printf("Loaded %d filter association(s)\n", len(coreRegistry.associates)) +} + +func (cr CoreRegistry) RegisterFacade(name string, facade plugins.Facade) error { + if _, ok := cr.facades[name]; ok { + panic("Facade with name " + name + " is already registered!") + } + + cr.facades[name] = &facade + + return nil +} + +func (cr CoreRegistry) RegisterStorage(name string, storage plugins.Storage) error { + if _, ok := cr.stores[name]; ok { + panic("Storage with name " + name + " is already registered!") + } + + cr.stores[name] = &storage + + return nil +} + +func (cr CoreRegistry) RegisterFilter(name string, filter plugins.Filter) error { + if _, ok := cr.filters[name]; ok { + panic("Filter with name " + name + " is already registered!") + } + + cr.filters[name] = &filter + + return nil +} + +func (cr CoreRegistry) AssociateFilter(filter string, storage string) error { + if _, ok := cr.associates[filter]; !ok { + cr.associates[filter] = make([]string, 0) + } + + supportedStores := cr.associates[filter] + + for _, store := range supportedStores { + if store == storage { + panic("Filter " + filter + " is already associated with storage " + storage + "!") + } + } + + supportedStores = append(supportedStores, storage) + cr.associates[filter] = supportedStores + + return nil +} + +func (cr CoreRegistry) GetStore(store string) *plugins.Storage { + return cr.stores[store] +} diff --git a/resource/resource.go b/resource/resource.go index 5680ff2..a52594e 100644 --- a/resource/resource.go +++ b/resource/resource.go @@ -1,15 +1,27 @@ package resource import ( - "github.com/ResourceAPI/Core/plugins" - "github.com/ResourceAPI/Core/schema" + "github.com/ResourceAPI/Core/registry" + "github.com/ResourceAPI/Interface/plugins" + "github.com/ResourceAPI/Interface/resource" + "github.com/ResourceAPI/Interface/schema" ) -func GetStore(resource string) *plugins.Storage { - return plugins.GetStore(schema.GetSchema(resource).Meta.Store) +type CoreProcessor struct { } -func GetResources() []string { +var coreProcessor *CoreProcessor + +func InitializeResources() { + coreProcessor = &CoreProcessor{} + resource.SetProcessor(coreProcessor) +} + +func (cp CoreProcessor) GetStore(resource string) *plugins.Storage { + return registry.GetRegistryInternal().GetStore(schema.GetProcessor().GetSchema(resource).Meta.Store) +} + +func (cp CoreProcessor) GetResources() []string { // TODO return []string{} } diff --git a/schema/schema.go b/schema/schema.go index edeba99..1671c60 100644 --- a/schema/schema.go +++ b/schema/schema.go @@ -8,21 +8,21 @@ import ( "os" "strings" + schemaInt "github.com/ResourceAPI/Interface/schema" + "github.com/xeipuuv/gojsonschema" ) -type Schema struct { - Data gojsonschema.JSONLoader - Source map[string]interface{} - Meta ResourceMeta +type CoreProcessor struct { + schemas map[string]CoreSchema } -type ResourceMeta struct { - Resource string `json:"resource"` - Store string `json:"store"` -} +var coreProcessor *CoreProcessor -var schemas = make(map[string]Schema) +type CoreSchema struct { + Parent schemaInt.Schema + Data gojsonschema.JSONLoader +} func InitializeSchemas() { files, err := ioutil.ReadDir("./resources") @@ -31,6 +31,12 @@ func InitializeSchemas() { panic(err) } + coreProcessor = &CoreProcessor{ + schemas: make(map[string]CoreSchema), + } + + schemaInt.SetProcessor(coreProcessor) + for _, f := range files { if strings.HasSuffix(f.Name(), ".json") { bytes, _ := ioutil.ReadFile("./resources/" + f.Name()) @@ -51,7 +57,7 @@ func InitializeSchemas() { } temp, _ := json.Marshal(source["meta"]) - meta := new(ResourceMeta) + meta := new(schemaInt.ResourceMeta) err = json.Unmarshal(temp, &meta) if err != nil { @@ -60,10 +66,12 @@ func InitializeSchemas() { continue } - s := Schema{ - Data: schema, - Source: source, - Meta: *meta, + s := CoreSchema{ + Data: schema, + Parent: schemaInt.Schema{ + Source: source, + Meta: *meta, + }, } if err != nil { @@ -72,20 +80,20 @@ func InitializeSchemas() { continue } - schemas[meta.Resource] = s + coreProcessor.schemas[meta.Resource] = s } } - fmt.Printf("Loaded %d schema(s)\n", len(schemas)) + fmt.Printf("Loaded %d schema(s)\n", len(coreProcessor.schemas)) } -func ResourceExists(resource string) bool { - _, ok := schemas[resource] +func (cp CoreProcessor) ResourceExists(resource string) bool { + _, ok := cp.schemas[resource] return ok } -func ResourceValid(resource string, data string) (bool, error) { - result, err := gojsonschema.Validate(schemas[resource].Data, gojsonschema.NewStringLoader(data)) +func (cp CoreProcessor) ResourceValid(resource string, data string) (bool, error) { + result, err := gojsonschema.Validate(cp.schemas[resource].Data, gojsonschema.NewStringLoader(data)) if err != nil { return false, err @@ -108,11 +116,11 @@ func ResourceValid(resource string, data string) (bool, error) { return false, errors.New(errs) } -func GetSchema(resource string) *Schema { - if _, ok := schemas[resource]; !ok { +func (cp CoreProcessor) GetSchema(resource string) *schemaInt.Schema { + if _, ok := cp.schemas[resource]; !ok { return nil } - schema := schemas[resource] - return &schema + schema := cp.schemas[resource] + return &(schema.Parent) } From 336198bcc10c5a9ab323a1ca56f76a1626856304 Mon Sep 17 00:00:00 2001 From: Vilsol Date: Sun, 14 Oct 2018 09:13:34 +0300 Subject: [PATCH 03/10] Add License, update Readme --- LICENSE | 201 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 9 +++ 2 files changed, 210 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6730d53 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018 ResourceAPI Team + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/README.md b/README.md index 33fb9dd..5ebb6c9 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,12 @@ # ResourceAPI A generic data storage API with multiple entrypoints (REST, GraphQL, WS, GRPC, etc), and supports multiple backends (Flatfile, SQLite, SQL, NoSQL, etc). + +### Competitors + +There are various competitors in this field, where each of them provide various benefits. Here are ours: + +* Completely FOSS without any locked features +* Self-hosted, which means compliance with local data storage regulations +* Modular with support for various storage types and usage scenarios +* Stateless design for scalability \ No newline at end of file From 643333794204d199fdad0629042e9482578d5db2 Mon Sep 17 00:00:00 2001 From: Vilsol Date: Wed, 17 Oct 2018 22:15:57 +0300 Subject: [PATCH 04/10] Refactor to StratoAPI --- LICENSE | 2 +- README.md | 2 +- api.go | 8 ++++---- cmd/cli/main.go | 2 +- go.mod | 6 +++--- registry/registry.go | 4 ++-- resource/resource.go | 8 ++++---- schema/schema.go | 2 +- 8 files changed, 17 insertions(+), 17 deletions(-) diff --git a/LICENSE b/LICENSE index 6730d53..94c6a28 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2018 ResourceAPI Team + Copyright 2018 StratoAPI Team Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index 5ebb6c9..b45be7f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ResourceAPI +# StratoAPI Core A generic data storage API with multiple entrypoints (REST, GraphQL, WS, GRPC, etc), and supports multiple backends (Flatfile, SQLite, SQL, NoSQL, etc). diff --git a/api.go b/api.go index fd25d44..515cc5f 100644 --- a/api.go +++ b/api.go @@ -1,10 +1,10 @@ package Core import ( - "github.com/ResourceAPI/Core/config" - "github.com/ResourceAPI/Core/registry" - "github.com/ResourceAPI/Core/resource" - "github.com/ResourceAPI/Core/schema" + "github.com/StratoAPI/Core/config" + "github.com/StratoAPI/Core/registry" + "github.com/StratoAPI/Core/resource" + "github.com/StratoAPI/Core/schema" ) func Run() { diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 9952c4b..dc54a88 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -1,6 +1,6 @@ package main -import "github.com/ResourceAPI/Core" +import "github.com/StratoAPI/Core" func main() { Core.Run() diff --git a/go.mod b/go.mod index 82a13db..6424178 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,9 @@ -module github.com/ResourceAPI/Core +module github.com/StratoAPI/Core -replace github.com/ResourceAPI/Interface v0.0.0 => ../Interface +replace github.com/StratoAPI/Interface v0.0.0 => ../Interface require ( - github.com/ResourceAPI/Interface v0.0.0 + github.com/StratoAPI/Interface v0.0.0 github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/testify v1.2.2 // indirect diff --git a/registry/registry.go b/registry/registry.go index 93a2b8a..e0354e3 100644 --- a/registry/registry.go +++ b/registry/registry.go @@ -5,8 +5,8 @@ import ( "io/ioutil" "plugin" - "github.com/ResourceAPI/Core/config" - "github.com/ResourceAPI/Interface/plugins" + "github.com/StratoAPI/Core/config" + "github.com/StratoAPI/Interface/plugins" ) type CoreRegistry struct { diff --git a/resource/resource.go b/resource/resource.go index a52594e..ba76861 100644 --- a/resource/resource.go +++ b/resource/resource.go @@ -1,10 +1,10 @@ package resource import ( - "github.com/ResourceAPI/Core/registry" - "github.com/ResourceAPI/Interface/plugins" - "github.com/ResourceAPI/Interface/resource" - "github.com/ResourceAPI/Interface/schema" + "github.com/StratoAPI/Core/registry" + "github.com/StratoAPI/Interface/plugins" + "github.com/StratoAPI/Interface/resource" + "github.com/StratoAPI/Interface/schema" ) type CoreProcessor struct { diff --git a/schema/schema.go b/schema/schema.go index 1671c60..eb08b4d 100644 --- a/schema/schema.go +++ b/schema/schema.go @@ -8,7 +8,7 @@ import ( "os" "strings" - schemaInt "github.com/ResourceAPI/Interface/schema" + schemaInt "github.com/StratoAPI/Interface/schema" "github.com/xeipuuv/gojsonschema" ) From a6a72c75a84a5029a3a9d74fa046fc059aa7c973 Mon Sep 17 00:00:00 2001 From: Vilsol Date: Mon, 22 Oct 2018 03:42:07 +0300 Subject: [PATCH 05/10] Add filtering, add simple filter --- .gitignore | 3 ++- Makefile | 4 +++ api.go | 3 +++ filter/filter.go | 45 +++++++++++++++++++++++++++++++ filter/simple.go | 63 ++++++++++++++++++++++++++++++++++++++++++++ go.mod | 4 +-- go.sum | 4 +++ registry/registry.go | 12 ++++++++- resource/resource.go | 32 +++++++++++++++++++++- schema/schema.go | 10 ++++++- 10 files changed, 173 insertions(+), 7 deletions(-) create mode 100644 filter/filter.go create mode 100644 filter/simple.go diff --git a/.gitignore b/.gitignore index 9aeb577..1630da0 100644 --- a/.gitignore +++ b/.gitignore @@ -172,4 +172,5 @@ $RECYCLE.BIN/ # End of https://www.gitignore.io/api/go,osx,linux,windows,jetbrains+all config.json -/core \ No newline at end of file +/core +data.json \ No newline at end of file diff --git a/Makefile b/Makefile index 9fe3638..aaa93a4 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ GOCMD=go GOBUILD=$(GOCMD) build +GOMOD=$(GOCMD) mod BINARY_NAME=core all: build @@ -7,6 +8,9 @@ all: build build: $(GOBUILD) -o $(BINARY_NAME) cmd/cli/main.go +tidy: + $(GOMOD) tidy + run: $(MAKE) build ./$(BINARY_NAME) \ No newline at end of file diff --git a/api.go b/api.go index 515cc5f..a5a7def 100644 --- a/api.go +++ b/api.go @@ -2,6 +2,7 @@ package Core import ( "github.com/StratoAPI/Core/config" + "github.com/StratoAPI/Core/filter" "github.com/StratoAPI/Core/registry" "github.com/StratoAPI/Core/resource" "github.com/StratoAPI/Core/schema" @@ -10,8 +11,10 @@ import ( func Run() { // Initialize Core config.InitializeConfig() + registry.InitializeRegistry() schema.InitializeSchemas() resource.InitializeResources() + filter.InitializeFilters() // Initialize Plugins registry.InitializePlugins() diff --git a/filter/filter.go b/filter/filter.go new file mode 100644 index 0000000..bcee21a --- /dev/null +++ b/filter/filter.go @@ -0,0 +1,45 @@ +package filter + +import ( + "errors" + "github.com/StratoAPI/Core/registry" + "github.com/StratoAPI/Interface/filter" +) + +type CoreProcessor struct { +} + +var coreProcessor *CoreProcessor + +func InitializeFilters() { + coreProcessor = &CoreProcessor{} + filter.SetProcessor(coreProcessor) + registerSimpleFilter() +} + +func (cp CoreProcessor) FilterExists(filter string) bool { + return registry.GetRegistryInternal().GetFilter(filter) != nil +} + +func (cp CoreProcessor) CreateFilter(filter string) interface{} { + f := registry.GetRegistryInternal().GetFilter(filter) + if f == nil { + return nil + } + + created, err := (*f).CreateFilter(filter) + + if err != nil { + panic(err) + } + + return created +} + +func (cp CoreProcessor) ValidateFilter(filter filter.ProcessedFilter) (bool, error) { + f := registry.GetRegistryInternal().GetFilter(filter.Type) + if f == nil { + return false, errors.New("filter not found") + } + return (*f).ValidateFilter(filter) +} diff --git a/filter/simple.go b/filter/simple.go new file mode 100644 index 0000000..31e9dd5 --- /dev/null +++ b/filter/simple.go @@ -0,0 +1,63 @@ +package filter + +import ( + "errors" + "github.com/StratoAPI/Core/registry" + "github.com/StratoAPI/Interface/filter" + "reflect" +) + +func registerSimpleFilter() { + registry.GetRegistryInternal().RegisterFilter("simple", &SimpleFilter{}) +} + +type SimpleFilter struct { +} + +func (sf SimpleFilter) Initialize() error { + return nil +} + +func (sf SimpleFilter) Start() error { + return nil +} + +func (sf SimpleFilter) Stop() error { + return nil +} + +func (sf SimpleFilter) ValidateFilter(processed filter.ProcessedFilter) (bool, error) { + simple, ok := processed.Data.(*filter.Simple) + + if !ok { + return false, errors.New("data does not match filter type") + } + + if !simple.Operation.Valid() { + return false, errors.New("filter operation not valid") + } + + if simple.Operation != filter.OpEQ && simple.Operation != filter.OpNEQ { + k := reflect.ValueOf(simple.Value).Type().Kind() + if k == reflect.Invalid || + k == reflect.Bool || + k == reflect.Array || + k == reflect.Chan || + k == reflect.Func || + k == reflect.Interface || + k == reflect.Map || + k == reflect.Ptr || + k == reflect.Slice || + k == reflect.String || + k == reflect.Struct || + k == reflect.UnsafePointer { + return false, errors.New("filter operation can only be applied to numbers") + } + } + + return true, nil +} + +func (sf SimpleFilter) CreateFilter(_ string) (interface{}, error) { + return &filter.Simple{}, nil +} diff --git a/go.mod b/go.mod index 6424178..af577f1 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,7 @@ module github.com/StratoAPI/Core -replace github.com/StratoAPI/Interface v0.0.0 => ../Interface - require ( - github.com/StratoAPI/Interface v0.0.0 + github.com/StratoAPI/Interface v0.0.6 github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/testify v1.2.2 // indirect diff --git a/go.sum b/go.sum index f9bfe51..74a0b00 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,7 @@ +github.com/StratoAPI/Interface v0.0.5 h1:pgQcXVIOAzC6HtZnVk67FCIBVNG7mgDwX3T2ZPlIhzQ= +github.com/StratoAPI/Interface v0.0.5/go.mod h1:jLlD9bBfAuFE0VN6FkrvX3cMhuDnbikzonCMprOLsyw= +github.com/StratoAPI/Interface v0.0.6 h1:yZ3mzdy0zLDZKnXdpoD0jaJmBGaBOtT1nRedujW4O0s= +github.com/StratoAPI/Interface v0.0.6/go.mod h1:jLlD9bBfAuFE0VN6FkrvX3cMhuDnbikzonCMprOLsyw= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= diff --git a/registry/registry.go b/registry/registry.go index e0354e3..4148704 100644 --- a/registry/registry.go +++ b/registry/registry.go @@ -22,14 +22,16 @@ func GetRegistryInternal() *CoreRegistry { return coreRegistry } -func InitializePlugins() { +func InitializeRegistry() { coreRegistry = &CoreRegistry{ facades: make(map[string]*plugins.Facade), stores: make(map[string]*plugins.Storage), filters: make(map[string]*plugins.Filter), associates: make(map[string][]string), } +} +func InitializePlugins() { files, err := ioutil.ReadDir(config.Get().PluginDirectory) if err != nil { @@ -143,3 +145,11 @@ func (cr CoreRegistry) AssociateFilter(filter string, storage string) error { func (cr CoreRegistry) GetStore(store string) *plugins.Storage { return cr.stores[store] } + +func (cr CoreRegistry) GetFilter(filter string) *plugins.Filter { + return cr.filters[filter] +} + +func (cr CoreRegistry) GetAssociates(filter string) []string { + return cr.associates[filter] +} diff --git a/resource/resource.go b/resource/resource.go index ba76861..9d08ec5 100644 --- a/resource/resource.go +++ b/resource/resource.go @@ -1,7 +1,9 @@ package resource import ( + "errors" "github.com/StratoAPI/Core/registry" + "github.com/StratoAPI/Interface/filter" "github.com/StratoAPI/Interface/plugins" "github.com/StratoAPI/Interface/resource" "github.com/StratoAPI/Interface/schema" @@ -21,7 +23,35 @@ func (cp CoreProcessor) GetStore(resource string) *plugins.Storage { return registry.GetRegistryInternal().GetStore(schema.GetProcessor().GetSchema(resource).Meta.Store) } -func (cp CoreProcessor) GetResources() []string { +func (cp CoreProcessor) GetResourceList() []string { // TODO return []string{} } + +func (cp CoreProcessor) GetResources(resource string, filters []filter.ProcessedFilter) ([]map[string]interface{}, error) { + resourceStore := schema.GetProcessor().GetSchema(resource).Meta.Store + + for _, f := range filters { + associates := registry.GetRegistryInternal().GetAssociates(f.Type) + + if len(associates) == 0 { + return nil, errors.New("the store does not support a provided filter") + } + + found := false + for _, store := range associates { + if store == resourceStore { + found = true + break + } + } + + if found { + break + } + + return nil, errors.New("the store does not support a provided filter") + } + + return (*registry.GetRegistryInternal().GetStore(schema.GetProcessor().GetSchema(resource).Meta.Store)).GetResources(resource, filters) +} diff --git a/schema/schema.go b/schema/schema.go index eb08b4d..cdff618 100644 --- a/schema/schema.go +++ b/schema/schema.go @@ -93,7 +93,15 @@ func (cp CoreProcessor) ResourceExists(resource string) bool { } func (cp CoreProcessor) ResourceValid(resource string, data string) (bool, error) { - result, err := gojsonschema.Validate(cp.schemas[resource].Data, gojsonschema.NewStringLoader(data)) + return cp.validateSchema(resource, gojsonschema.NewStringLoader(data)) +} + +func (cp CoreProcessor) ResourceValidGo(resource string, data interface{}) (bool, error) { + return cp.validateSchema(resource, gojsonschema.NewGoLoader(data)) +} + +func (cp CoreProcessor) validateSchema(resource string, loader gojsonschema.JSONLoader) (bool, error) { + result, err := gojsonschema.Validate(cp.schemas[resource].Data, loader) if err != nil { return false, err From 3d7a33be4a7d367ff7304f121bd2c85e55da85ae Mon Sep 17 00:00:00 2001 From: Vilsol Date: Tue, 23 Oct 2018 04:49:52 +0300 Subject: [PATCH 06/10] Graceful shutdown, panic on timeout, check stores for existence --- api.go | 3 +++ registry/manager.go | 39 +++++++++++++++++++++++++++++++++------ schema/schema.go | 9 +++++++++ 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/api.go b/api.go index a5a7def..3fbad52 100644 --- a/api.go +++ b/api.go @@ -22,6 +22,9 @@ func Run() { registry.InitializeFilters() registry.InitializeFacades() + // Validate Schema Settings + schema.ValidateSchemas() + // Start up stores registry.StartStores() diff --git a/registry/manager.go b/registry/manager.go index 520e84f..006eca2 100644 --- a/registry/manager.go +++ b/registry/manager.go @@ -1,7 +1,11 @@ package registry import ( + "fmt" + "os" + "os/signal" "sync" + "time" ) var storageWaitGroup sync.WaitGroup @@ -34,7 +38,6 @@ func StartStores() { } func StopStores() { - // TODO 30s timeout for _, store := range coreRegistry.stores { err := (*store).Stop() @@ -70,7 +73,6 @@ func StartFacades() { } func StopFacades() { - // TODO 30s timeout for _, facade := range coreRegistry.facades { err := (*facade).Stop() @@ -106,7 +108,6 @@ func StartFilters() { } func StopFilters() { - // TODO 30s timeout for _, filter := range coreRegistry.filters { err := (*filter).Stop() @@ -117,7 +118,33 @@ func StopFilters() { } func WaitForGoroutines() { - facadeWaitGroup.Wait() - storageWaitGroup.Wait() - filtersWaitGroup.Wait() + done := make(chan bool, 1) + + stop := make(chan os.Signal, 1) + signal.Notify(stop, os.Interrupt) + + go func() { + facadeWaitGroup.Wait() + storageWaitGroup.Wait() + filtersWaitGroup.Wait() + done <- true + }() + + <-stop + + fmt.Println("Shutting down the server...") + + go func() { + StopFacades() + StopStores() + StopFilters() + }() + + select { + // TODO Make time configurable + case <-time.After(30 * time.Second): + panic("Graceful shutdown failed!") + case <-done: + fmt.Println("Server shut down") + } } diff --git a/schema/schema.go b/schema/schema.go index cdff618..73940dc 100644 --- a/schema/schema.go +++ b/schema/schema.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/StratoAPI/Core/registry" "io/ioutil" "os" "strings" @@ -87,6 +88,14 @@ func InitializeSchemas() { fmt.Printf("Loaded %d schema(s)\n", len(coreProcessor.schemas)) } +func ValidateSchemas() { + for _, schema := range coreProcessor.schemas { + if registry.GetRegistryInternal().GetStore(schema.Parent.Meta.Store) == nil { + panic("resource " + schema.Parent.Meta.Resource + " uses an unsupported store: " + schema.Parent.Meta.Store) + } + } +} + func (cp CoreProcessor) ResourceExists(resource string) bool { _, ok := cp.schemas[resource] return ok From e6aeaeffb5accae948c410731d122f250c979536 Mon Sep 17 00:00:00 2001 From: Vilsol Date: Tue, 23 Oct 2018 07:01:33 +0300 Subject: [PATCH 07/10] Complete REST and Flatfile-JSON implementations --- go.mod | 2 +- go.sum | 6 ++---- resource/resource.go | 44 +++++++++++++++++++++++++++++++++++++++++--- schema/schema.go | 36 ++++++++++++++++++++++++++++-------- 4 files changed, 72 insertions(+), 16 deletions(-) diff --git a/go.mod b/go.mod index af577f1..c66d9a4 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,7 @@ module github.com/StratoAPI/Core require ( - github.com/StratoAPI/Interface v0.0.6 + github.com/StratoAPI/Interface v0.0.8 github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/testify v1.2.2 // indirect diff --git a/go.sum b/go.sum index 74a0b00..dad5dd3 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,5 @@ -github.com/StratoAPI/Interface v0.0.5 h1:pgQcXVIOAzC6HtZnVk67FCIBVNG7mgDwX3T2ZPlIhzQ= -github.com/StratoAPI/Interface v0.0.5/go.mod h1:jLlD9bBfAuFE0VN6FkrvX3cMhuDnbikzonCMprOLsyw= -github.com/StratoAPI/Interface v0.0.6 h1:yZ3mzdy0zLDZKnXdpoD0jaJmBGaBOtT1nRedujW4O0s= -github.com/StratoAPI/Interface v0.0.6/go.mod h1:jLlD9bBfAuFE0VN6FkrvX3cMhuDnbikzonCMprOLsyw= +github.com/StratoAPI/Interface v0.0.8 h1:iOh/54yilmULpuS1t849SU91HTlLYyt5Dc1a8rFb5Bg= +github.com/StratoAPI/Interface v0.0.8/go.mod h1:jLlD9bBfAuFE0VN6FkrvX3cMhuDnbikzonCMprOLsyw= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= diff --git a/resource/resource.go b/resource/resource.go index 9d08ec5..ef5498a 100644 --- a/resource/resource.go +++ b/resource/resource.go @@ -31,11 +31,49 @@ func (cp CoreProcessor) GetResourceList() []string { func (cp CoreProcessor) GetResources(resource string, filters []filter.ProcessedFilter) ([]map[string]interface{}, error) { resourceStore := schema.GetProcessor().GetSchema(resource).Meta.Store + err := checkStoreFilterAssociates(resourceStore, filters) + + if err != nil { + return nil, err + } + + return (*registry.GetRegistryInternal().GetStore(resourceStore)).GetResources(resource, filters) +} + +func (cp CoreProcessor) CreateResources(resource string, data []map[string]interface{}) error { + return (*registry.GetRegistryInternal().GetStore(schema.GetProcessor().GetSchema(resource).Meta.Store)).CreateResources(resource, data) +} + +func (cp CoreProcessor) UpdateResources(resource string, data map[string]interface{}, filters []filter.ProcessedFilter) error { + resourceStore := schema.GetProcessor().GetSchema(resource).Meta.Store + + err := checkStoreFilterAssociates(resourceStore, filters) + + if err != nil { + return err + } + + return (*registry.GetRegistryInternal().GetStore(resourceStore)).UpdateResources(resource, data, filters) +} + +func (cp CoreProcessor) DeleteResources(resource string, filters []filter.ProcessedFilter) error { + resourceStore := schema.GetProcessor().GetSchema(resource).Meta.Store + + err := checkStoreFilterAssociates(resourceStore, filters) + + if err != nil { + return err + } + + return (*registry.GetRegistryInternal().GetStore(resourceStore)).DeleteResources(resource, filters) +} + +func checkStoreFilterAssociates(resourceStore string, filters []filter.ProcessedFilter) error { for _, f := range filters { associates := registry.GetRegistryInternal().GetAssociates(f.Type) if len(associates) == 0 { - return nil, errors.New("the store does not support a provided filter") + return errors.New("the store does not support a provided filter") } found := false @@ -50,8 +88,8 @@ func (cp CoreProcessor) GetResources(resource string, filters []filter.Processed break } - return nil, errors.New("the store does not support a provided filter") + return errors.New("the store does not support a provided filter") } - return (*registry.GetRegistryInternal().GetStore(schema.GetProcessor().GetSchema(resource).Meta.Store)).GetResources(resource, filters) + return nil } diff --git a/schema/schema.go b/schema/schema.go index 73940dc..271a7f3 100644 --- a/schema/schema.go +++ b/schema/schema.go @@ -22,7 +22,7 @@ var coreProcessor *CoreProcessor type CoreSchema struct { Parent schemaInt.Schema - Data gojsonschema.JSONLoader + Data *gojsonschema.Schema } func InitializeSchemas() { @@ -67,8 +67,16 @@ func InitializeSchemas() { continue } + newSchema, err := gojsonschema.NewSchema(schema) + + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to load compile resource schema %s:\n", f.Name()) + fmt.Fprintln(os.Stderr, err) + continue + } + s := CoreSchema{ - Data: schema, + Data: newSchema, Parent: schemaInt.Schema{ Source: source, Meta: *meta, @@ -101,16 +109,16 @@ func (cp CoreProcessor) ResourceExists(resource string) bool { return ok } -func (cp CoreProcessor) ResourceValid(resource string, data string) (bool, error) { - return cp.validateSchema(resource, gojsonschema.NewStringLoader(data)) +func (cp CoreProcessor) ResourceValid(resource string, data string, required bool) (bool, error) { + return cp.validateSchema(resource, gojsonschema.NewStringLoader(data), required) } -func (cp CoreProcessor) ResourceValidGo(resource string, data interface{}) (bool, error) { - return cp.validateSchema(resource, gojsonschema.NewGoLoader(data)) +func (cp CoreProcessor) ResourceValidGo(resource string, data interface{}, required bool) (bool, error) { + return cp.validateSchema(resource, gojsonschema.NewGoLoader(data), required) } -func (cp CoreProcessor) validateSchema(resource string, loader gojsonschema.JSONLoader) (bool, error) { - result, err := gojsonschema.Validate(cp.schemas[resource].Data, loader) +func (cp CoreProcessor) validateSchema(resource string, loader gojsonschema.JSONLoader, required bool) (bool, error) { + result, err := cp.schemas[resource].Data.Validate(loader) if err != nil { return false, err @@ -121,8 +129,16 @@ func (cp CoreProcessor) validateSchema(resource string, loader gojsonschema.JSON } errs := "" + validErrors := false for i, err := range result.Errors() { + if !required && err.Type() == "required" { + continue + } + + validErrors = true + + err.Type() if i > 0 { errs += ", " } @@ -130,6 +146,10 @@ func (cp CoreProcessor) validateSchema(resource string, loader gojsonschema.JSON errs += err.String() } + if !validErrors { + return true, nil + } + return false, errors.New(errs) } From cd93489b47fd632b03b958ae0c6744cb8fb9321a Mon Sep 17 00:00:00 2001 From: Vilsol Date: Sun, 28 Oct 2018 22:57:19 +0200 Subject: [PATCH 08/10] Add middleware support --- api.go | 6 +++++ go.mod | 2 +- go.sum | 4 +-- middleware/middleware.go | 55 ++++++++++++++++++++++++++++++++++++++++ registry/manager.go | 38 +++++++++++++++++++++++++++ registry/registry.go | 32 +++++++++++++++++------ 6 files changed, 126 insertions(+), 11 deletions(-) create mode 100644 middleware/middleware.go diff --git a/api.go b/api.go index 3fbad52..642b9c6 100644 --- a/api.go +++ b/api.go @@ -3,6 +3,7 @@ package Core import ( "github.com/StratoAPI/Core/config" "github.com/StratoAPI/Core/filter" + "github.com/StratoAPI/Core/middleware" "github.com/StratoAPI/Core/registry" "github.com/StratoAPI/Core/resource" "github.com/StratoAPI/Core/schema" @@ -15,12 +16,14 @@ func Run() { schema.InitializeSchemas() resource.InitializeResources() filter.InitializeFilters() + middleware.InitializeMiddleware() // Initialize Plugins registry.InitializePlugins() registry.InitializeStores() registry.InitializeFilters() registry.InitializeFacades() + registry.InitializeMiddlewares() // Validate Schema Settings schema.ValidateSchemas() @@ -28,6 +31,9 @@ func Run() { // Start up stores registry.StartStores() + // Start up middlewares + registry.StartMiddlewares() + // Start up filters registry.StartFilters() diff --git a/go.mod b/go.mod index c66d9a4..9be07b0 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,7 @@ module github.com/StratoAPI/Core require ( - github.com/StratoAPI/Interface v0.0.8 + github.com/StratoAPI/Interface v0.0.11 github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/testify v1.2.2 // indirect diff --git a/go.sum b/go.sum index dad5dd3..74a5457 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/StratoAPI/Interface v0.0.8 h1:iOh/54yilmULpuS1t849SU91HTlLYyt5Dc1a8rFb5Bg= -github.com/StratoAPI/Interface v0.0.8/go.mod h1:jLlD9bBfAuFE0VN6FkrvX3cMhuDnbikzonCMprOLsyw= +github.com/StratoAPI/Interface v0.0.11 h1:oms2ia6Rl6TBC98umloNGthx5+aFPDP1oK+1aMog7Ds= +github.com/StratoAPI/Interface v0.0.11/go.mod h1:jLlD9bBfAuFE0VN6FkrvX3cMhuDnbikzonCMprOLsyw= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= diff --git a/middleware/middleware.go b/middleware/middleware.go new file mode 100644 index 0000000..6f8ab28 --- /dev/null +++ b/middleware/middleware.go @@ -0,0 +1,55 @@ +package middleware + +import ( + "github.com/StratoAPI/Core/registry" + "github.com/StratoAPI/Interface/middleware" + "github.com/StratoAPI/Interface/schema" +) + +type CoreProcessor struct { +} + +var coreProcessor *CoreProcessor + +func InitializeMiddleware() { + coreProcessor = &CoreProcessor{} + middleware.SetProcessor(coreProcessor) +} + +func (cp CoreProcessor) Request(resource string, headers map[string][]string) *middleware.RequestResponse { + middlewares := schema.GetProcessor().GetSchema(resource).Meta.Middlewares + + for _, mwMeta := range middlewares { + mw := registry.GetRegistryInternal().GetMiddleware(mwMeta.Type) + if mw == nil { + panic("Middleware " + mwMeta.Type + " is nil") + } + + request := (*mw).Request(resource, headers, mwMeta.Data) + if request != nil { + return request + } + } + + return nil +} + +func (cp CoreProcessor) Response(resource string, headers map[string][]string, data []map[string]interface{}) ([]map[string]interface{}, *middleware.RequestResponse) { + middlewares := schema.GetProcessor().GetSchema(resource).Meta.Middlewares + + for _, mwMeta := range middlewares { + mw := registry.GetRegistryInternal().GetMiddleware(mwMeta.Type) + if mw == nil { + panic("Middleware " + mwMeta.Type + " is nil") + } + + result, response := (*mw).Response(resource, headers, data, mwMeta.Data) + if response != nil { + return nil, response + } + + data = result + } + + return data, nil +} diff --git a/registry/manager.go b/registry/manager.go index 006eca2..fc8f5a4 100644 --- a/registry/manager.go +++ b/registry/manager.go @@ -11,6 +11,7 @@ import ( var storageWaitGroup sync.WaitGroup var facadeWaitGroup sync.WaitGroup var filtersWaitGroup sync.WaitGroup +var middlewareWaitGroup sync.WaitGroup func InitializeStores() { for _, store := range coreRegistry.stores { @@ -117,6 +118,41 @@ func StopFilters() { } } +func InitializeMiddlewares() { + for _, middleware := range coreRegistry.middlewares { + err := (*middleware).Initialize() + + if err != nil { + panic(err) + } + } +} + +func StartMiddlewares() { + storageWaitGroup.Add(len(coreRegistry.middlewares)) + + for _, middleware := range coreRegistry.middlewares { + go func() { + defer storageWaitGroup.Done() + err := (*middleware).Start() + + if err != nil { + panic(err) + } + }() + } +} + +func StopMiddlewares() { + for _, middleware := range coreRegistry.middlewares { + err := (*middleware).Stop() + + if err != nil { + panic(err) + } + } +} + func WaitForGoroutines() { done := make(chan bool, 1) @@ -127,6 +163,7 @@ func WaitForGoroutines() { facadeWaitGroup.Wait() storageWaitGroup.Wait() filtersWaitGroup.Wait() + middlewareWaitGroup.Wait() done <- true }() @@ -138,6 +175,7 @@ func WaitForGoroutines() { StopFacades() StopStores() StopFilters() + StopMiddlewares() }() select { diff --git a/registry/registry.go b/registry/registry.go index 4148704..c963cbb 100644 --- a/registry/registry.go +++ b/registry/registry.go @@ -10,10 +10,11 @@ import ( ) type CoreRegistry struct { - facades map[string]*plugins.Facade - stores map[string]*plugins.Storage - filters map[string]*plugins.Filter - associates map[string][]string + facades map[string]*plugins.Facade + stores map[string]*plugins.Storage + filters map[string]*plugins.Filter + middlewares map[string]*plugins.Middleware + associates map[string][]string } var coreRegistry *CoreRegistry @@ -24,10 +25,11 @@ func GetRegistryInternal() *CoreRegistry { func InitializeRegistry() { coreRegistry = &CoreRegistry{ - facades: make(map[string]*plugins.Facade), - stores: make(map[string]*plugins.Storage), - filters: make(map[string]*plugins.Filter), - associates: make(map[string][]string), + facades: make(map[string]*plugins.Facade), + stores: make(map[string]*plugins.Storage), + filters: make(map[string]*plugins.Filter), + middlewares: make(map[string]*plugins.Middleware), + associates: make(map[string][]string), } } @@ -123,6 +125,16 @@ func (cr CoreRegistry) RegisterFilter(name string, filter plugins.Filter) error return nil } +func (cr CoreRegistry) RegisterMiddleware(name string, middleware plugins.Middleware) error { + if _, ok := cr.middlewares[name]; ok { + panic("Middleware with name " + name + " is already registered!") + } + + cr.middlewares[name] = &middleware + + return nil +} + func (cr CoreRegistry) AssociateFilter(filter string, storage string) error { if _, ok := cr.associates[filter]; !ok { cr.associates[filter] = make([]string, 0) @@ -153,3 +165,7 @@ func (cr CoreRegistry) GetFilter(filter string) *plugins.Filter { func (cr CoreRegistry) GetAssociates(filter string) []string { return cr.associates[filter] } + +func (cr CoreRegistry) GetMiddleware(middleware string) *plugins.Middleware { + return cr.middlewares[middleware] +} From 86b9bbbacef43ea218ceea344bc94fae59fbd85d Mon Sep 17 00:00:00 2001 From: Vilsol Date: Tue, 30 Oct 2018 00:49:40 +0200 Subject: [PATCH 09/10] Custom registrable plugin configs --- api.go | 7 ++++++- config.example.json | 11 +---------- config/config.go | 45 +++++++++++++++++++++++++++++++++++++++++++- config/types.go | 15 +-------------- go.mod | 2 +- go.sum | 4 ++-- registry/registry.go | 23 ++++++++++++++++++---- 7 files changed, 74 insertions(+), 33 deletions(-) diff --git a/api.go b/api.go index 642b9c6..407f52b 100644 --- a/api.go +++ b/api.go @@ -19,7 +19,12 @@ func Run() { middleware.InitializeMiddleware() // Initialize Plugins - registry.InitializePlugins() + registry.InitializePlugins(config.Get().PluginDirectory) + + // Initialize Plugin Configs + config.InitializePluginConfigs() + + // Initialize Components registry.InitializeStores() registry.InitializeFilters() registry.InitializeFacades() diff --git a/config.example.json b/config.example.json index fe8d2df..450403b 100644 --- a/config.example.json +++ b/config.example.json @@ -1,13 +1,4 @@ { - "host": "0.0.0.0", - "port": 5020, - "plugin_directory": "plugins", - - "database": { - "type": "flatfile", - "flatfile": { - "location": "data.json" - } - } + "config_directory": "config" } \ No newline at end of file diff --git a/config/config.go b/config/config.go index 22506ac..836d108 100644 --- a/config/config.go +++ b/config/config.go @@ -3,6 +3,8 @@ package config import ( "encoding/json" "fmt" + "github.com/StratoAPI/Core/registry" + "io/ioutil" "os" ) @@ -14,12 +16,53 @@ func InitializeConfig() { panic(err) } + defer configFile.Close() + jsonParser := json.NewDecoder(configFile) if err = jsonParser.Decode(&config); err != nil { panic(err) } - fmt.Println("Config initialized") + if _, err := os.Stat(config.ConfigDirectory); os.IsNotExist(err) { + panic(err) + } + + fmt.Println("Configs initialized") +} + +func InitializePluginConfigs() { + configs := registry.GetRegistryInternal().GetConfigs() + + for name, conf := range configs { + configFile := config.ConfigDirectory + "/" + name + ".json" + structure := (*conf).CreateStructure() + if _, err := os.Stat(configFile); os.IsNotExist(err) { + bytes, err := json.MarshalIndent(structure, "", " ") + if err != nil { + panic(err) + } + + err = ioutil.WriteFile(configFile, bytes, 0644) + if err != nil { + panic(err) + } + } else { + configFile, err := os.Open(configFile) + if err != nil { + panic(err) + } + + jsonParser := json.NewDecoder(configFile) + if err = jsonParser.Decode(&structure); err != nil { + panic(err) + } + + configFile.Close() + } + (*conf).Set(structure) + } + + fmt.Println("Plugin Configs initialized") } func Get() *Config { diff --git a/config/types.go b/config/types.go index dfc6cd4..9da4962 100644 --- a/config/types.go +++ b/config/types.go @@ -1,19 +1,6 @@ package config type Config struct { - Host string `json:"host"` - Port uint32 `json:"port"` - PluginDirectory string `json:"plugin_directory"` - - Database Database `json:"database"` -} - -type Database struct { - Type string `json:"type"` - Flatfile Flatfile `json:"flatfile"` -} - -type Flatfile struct { - Location string `json:"location"` + ConfigDirectory string `json:"config_directory"` } diff --git a/go.mod b/go.mod index 9be07b0..8d18bb7 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,7 @@ module github.com/StratoAPI/Core require ( - github.com/StratoAPI/Interface v0.0.11 + github.com/StratoAPI/Interface v0.0.12 github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/testify v1.2.2 // indirect diff --git a/go.sum b/go.sum index 74a5457..f04f4f1 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/StratoAPI/Interface v0.0.11 h1:oms2ia6Rl6TBC98umloNGthx5+aFPDP1oK+1aMog7Ds= -github.com/StratoAPI/Interface v0.0.11/go.mod h1:jLlD9bBfAuFE0VN6FkrvX3cMhuDnbikzonCMprOLsyw= +github.com/StratoAPI/Interface v0.0.12 h1:cs7sy1isE7clYSoZWBeRuXsLRnX3eYLdBMpziRUVMM4= +github.com/StratoAPI/Interface v0.0.12/go.mod h1:jLlD9bBfAuFE0VN6FkrvX3cMhuDnbikzonCMprOLsyw= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= diff --git a/registry/registry.go b/registry/registry.go index c963cbb..71e6775 100644 --- a/registry/registry.go +++ b/registry/registry.go @@ -5,7 +5,6 @@ import ( "io/ioutil" "plugin" - "github.com/StratoAPI/Core/config" "github.com/StratoAPI/Interface/plugins" ) @@ -14,6 +13,7 @@ type CoreRegistry struct { stores map[string]*plugins.Storage filters map[string]*plugins.Filter middlewares map[string]*plugins.Middleware + configs map[string]*plugins.Config associates map[string][]string } @@ -29,12 +29,13 @@ func InitializeRegistry() { stores: make(map[string]*plugins.Storage), filters: make(map[string]*plugins.Filter), middlewares: make(map[string]*plugins.Middleware), + configs: make(map[string]*plugins.Config), associates: make(map[string][]string), } } -func InitializePlugins() { - files, err := ioutil.ReadDir(config.Get().PluginDirectory) +func InitializePlugins(pluginDirectory string) { + files, err := ioutil.ReadDir(pluginDirectory) if err != nil { panic(err) @@ -44,7 +45,7 @@ func InitializePlugins() { loadedPlugins := make([]plugins.Plugin, 0) for _, f := range files { - plug, err := plugin.Open(config.Get().PluginDirectory + "/" + f.Name()) + plug, err := plugin.Open(pluginDirectory + "/" + f.Name()) if err != nil { fmt.Println(err) continue @@ -135,6 +136,16 @@ func (cr CoreRegistry) RegisterMiddleware(name string, middleware plugins.Middle return nil } +func (cr CoreRegistry) RegisterConfig(name string, config plugins.Config) error { + if _, ok := cr.configs[name]; ok { + panic("Config with name " + name + " is already registered!") + } + + cr.configs[name] = &config + + return nil +} + func (cr CoreRegistry) AssociateFilter(filter string, storage string) error { if _, ok := cr.associates[filter]; !ok { cr.associates[filter] = make([]string, 0) @@ -169,3 +180,7 @@ func (cr CoreRegistry) GetAssociates(filter string) []string { func (cr CoreRegistry) GetMiddleware(middleware string) *plugins.Middleware { return cr.middlewares[middleware] } + +func (cr CoreRegistry) GetConfigs() map[string]*plugins.Config { + return cr.configs +} From 9b9cbf2ba1ca7c51ba8fc93ecf795834debf8981 Mon Sep 17 00:00:00 2001 From: Vilsol Date: Tue, 30 Oct 2018 01:07:32 +0200 Subject: [PATCH 10/10] Add default store config option --- config.example.json | 4 +++- config/types.go | 2 ++ resource/resource.go | 21 ++++++++++++++++----- schema/schema.go | 11 +++++++++-- 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/config.example.json b/config.example.json index 450403b..025ff7a 100644 --- a/config.example.json +++ b/config.example.json @@ -1,4 +1,6 @@ { "plugin_directory": "plugins", - "config_directory": "config" + "config_directory": "config", + + "default_store": "Flatfile-JSON" } \ No newline at end of file diff --git a/config/types.go b/config/types.go index 9da4962..ea299f9 100644 --- a/config/types.go +++ b/config/types.go @@ -3,4 +3,6 @@ package config type Config struct { PluginDirectory string `json:"plugin_directory"` ConfigDirectory string `json:"config_directory"` + + DefaultStore string `json:"default_store"` } diff --git a/resource/resource.go b/resource/resource.go index ef5498a..dd1686d 100644 --- a/resource/resource.go +++ b/resource/resource.go @@ -2,6 +2,7 @@ package resource import ( "errors" + "github.com/StratoAPI/Core/config" "github.com/StratoAPI/Core/registry" "github.com/StratoAPI/Interface/filter" "github.com/StratoAPI/Interface/plugins" @@ -19,8 +20,18 @@ func InitializeResources() { resource.SetProcessor(coreProcessor) } +func (cp CoreProcessor) GetStoreName(resource string) string { + resourceStore := schema.GetProcessor().GetSchema(resource).Meta.Store + + if resourceStore == "" { + resourceStore = config.Get().DefaultStore + } + + return resourceStore +} + func (cp CoreProcessor) GetStore(resource string) *plugins.Storage { - return registry.GetRegistryInternal().GetStore(schema.GetProcessor().GetSchema(resource).Meta.Store) + return registry.GetRegistryInternal().GetStore(cp.GetStoreName(resource)) } func (cp CoreProcessor) GetResourceList() []string { @@ -29,7 +40,7 @@ func (cp CoreProcessor) GetResourceList() []string { } func (cp CoreProcessor) GetResources(resource string, filters []filter.ProcessedFilter) ([]map[string]interface{}, error) { - resourceStore := schema.GetProcessor().GetSchema(resource).Meta.Store + resourceStore := cp.GetStoreName(resource) err := checkStoreFilterAssociates(resourceStore, filters) @@ -41,11 +52,11 @@ func (cp CoreProcessor) GetResources(resource string, filters []filter.Processed } func (cp CoreProcessor) CreateResources(resource string, data []map[string]interface{}) error { - return (*registry.GetRegistryInternal().GetStore(schema.GetProcessor().GetSchema(resource).Meta.Store)).CreateResources(resource, data) + return (*cp.GetStore(resource)).CreateResources(resource, data) } func (cp CoreProcessor) UpdateResources(resource string, data map[string]interface{}, filters []filter.ProcessedFilter) error { - resourceStore := schema.GetProcessor().GetSchema(resource).Meta.Store + resourceStore := cp.GetStoreName(resource) err := checkStoreFilterAssociates(resourceStore, filters) @@ -57,7 +68,7 @@ func (cp CoreProcessor) UpdateResources(resource string, data map[string]interfa } func (cp CoreProcessor) DeleteResources(resource string, filters []filter.ProcessedFilter) error { - resourceStore := schema.GetProcessor().GetSchema(resource).Meta.Store + resourceStore := cp.GetStoreName(resource) err := checkStoreFilterAssociates(resourceStore, filters) diff --git a/schema/schema.go b/schema/schema.go index 271a7f3..e3caf4e 100644 --- a/schema/schema.go +++ b/schema/schema.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/StratoAPI/Core/config" "github.com/StratoAPI/Core/registry" "io/ioutil" "os" @@ -98,8 +99,14 @@ func InitializeSchemas() { func ValidateSchemas() { for _, schema := range coreProcessor.schemas { - if registry.GetRegistryInternal().GetStore(schema.Parent.Meta.Store) == nil { - panic("resource " + schema.Parent.Meta.Resource + " uses an unsupported store: " + schema.Parent.Meta.Store) + selectedStore := schema.Parent.Meta.Store + + if selectedStore == "" { + selectedStore = config.Get().DefaultStore + } + + if registry.GetRegistryInternal().GetStore(selectedStore) == nil { + panic("resource " + schema.Parent.Meta.Resource + " uses an unsupported store: " + selectedStore) } } }