From a9bee0990c7baf71c1e3ccc1479e07148496a6b6 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 --- Makefile | 11 +++++++++++ nodes/resource.go | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8eae8b1 --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ +GOCMD=go +GOBUILD=$(GOCMD) build +BINARY_NAME=rest.so + +all: build + +build: + $(GOBUILD) -buildmode=plugin -o $(BINARY_NAME) main.go + +clean: + rm -f $(BINARY_NAME) \ No newline at end of file diff --git a/nodes/resource.go b/nodes/resource.go index 166cbca..9a73ee1 100644 --- a/nodes/resource.go +++ b/nodes/resource.go @@ -26,7 +26,6 @@ func getResource(r *http.Request) (interface{}, *GoLib.ErrorResponse) { } // TODO Filters - // TODO Error handling resources, _ := (*resource.GetStore(resourceName)).GetResources(resourceName, nil) return resources, nil } @@ -54,6 +53,7 @@ func storeResource(r *http.Request) (interface{}, *GoLib.ErrorResponse) { var data map[string]interface{} json.Unmarshal(body, &data) + // TODO Filters (*resource.GetStore(resourceName)).CreateResources(resourceName, []map[string]interface{}{data}) return nil, nil From d9bb7b53358e50c2bd96eb886f3cad68868ff778 Mon Sep 17 00:00:00 2001 From: Vilsol Date: Sat, 13 Oct 2018 06:48:31 +0300 Subject: [PATCH 02/10] Create an interface between core and plugins --- go.mod | 4 ++-- go.sum | 15 --------------- main.go | 4 ++-- nodes/resource.go | 15 +++++++-------- server/facade.go | 6 +++--- 5 files changed, 14 insertions(+), 30 deletions(-) diff --git a/go.mod b/go.mod index 71f3216..df61869 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,9 @@ module github.com/ResourceAPI/REST -replace github.com/ResourceAPI/Core v0.0.0 => ../Core +replace github.com/ResourceAPI/Interface v0.0.0 => ../Interface require ( - github.com/ResourceAPI/Core v0.0.0 + github.com/ResourceAPI/Interface v0.0.0 github.com/Vilsol/GoLib v0.0.7 github.com/gorilla/handlers v1.4.0 github.com/gorilla/mux v1.6.2 diff --git a/go.sum b/go.sum index e8b6337..1d1f1dc 100644 --- a/go.sum +++ b/go.sum @@ -1,23 +1,8 @@ -github.com/ResourceAPI/Core v0.0.0-20181002012618-cecf6969a977 h1:5WZQednlDzVdWUa4B94sTCLGUxyo77pW8AtGF3BAY5w= -github.com/ResourceAPI/Core v0.0.0-20181002012618-cecf6969a977/go.mod h1:5xNSzHhNYaEeSHIM16anvJcxVBWssVUMmHwvEeCSFmM= 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= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v0.0.0-20180816142147-da425ebb7609 h1:BcMExZAULPkihVZ7UJXK7t8rwGqisXFw75tILnafhBY= -github.com/xeipuuv/gojsonschema v0.0.0-20180816142147-da425ebb7609/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= diff --git a/main.go b/main.go index 79dad13..7951155 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,7 @@ package main import ( - "github.com/ResourceAPI/Core/plugins" + "github.com/ResourceAPI/Interface/plugins" "github.com/ResourceAPI/REST/server" ) @@ -12,7 +12,7 @@ func (RESTPlugin) Name() string { } func (RESTPlugin) Entrypoint() { - plugins.RegisterFacade("REST", &server.RESTFacade{}) + plugins.GetRegistry().RegisterFacade("REST", &server.RESTFacade{}) } var CorePlugin RESTPlugin diff --git a/nodes/resource.go b/nodes/resource.go index 9a73ee1..c4dc6eb 100644 --- a/nodes/resource.go +++ b/nodes/resource.go @@ -5,9 +5,8 @@ import ( "io/ioutil" "net/http" - "github.com/ResourceAPI/Core/resource" - - "github.com/ResourceAPI/Core/schema" + "github.com/ResourceAPI/Interface/resource" + "github.com/ResourceAPI/Interface/schema" "github.com/Vilsol/GoLib" "github.com/gorilla/mux" ) @@ -21,19 +20,19 @@ func RegisterResourceRoutes(router GoLib.RegisterRoute) { func getResource(r *http.Request) (interface{}, *GoLib.ErrorResponse) { resourceName := mux.Vars(r)["resource"] - if !schema.ResourceExists(resourceName) { + if !schema.GetProcessor().ResourceExists(resourceName) { return nil, &ErrorResourceDoesNotExist } // TODO Filters - resources, _ := (*resource.GetStore(resourceName)).GetResources(resourceName, nil) + resources, _ := (*resource.GetProcessor().GetStore(resourceName)).GetResources(resourceName, nil) return resources, nil } func storeResource(r *http.Request) (interface{}, *GoLib.ErrorResponse) { resourceName := mux.Vars(r)["resource"] - if !schema.ResourceExists(resourceName) { + if !schema.GetProcessor().ResourceExists(resourceName) { return nil, &ErrorResourceDoesNotExist } @@ -42,7 +41,7 @@ func storeResource(r *http.Request) (interface{}, *GoLib.ErrorResponse) { return nil, &ErrorCouldNotReadBody } - valid, err := schema.ResourceValid(resourceName, string(body)) + valid, err := schema.GetProcessor().ResourceValid(resourceName, string(body)) if !valid { resp := ErrorResourceInvalid @@ -54,7 +53,7 @@ func storeResource(r *http.Request) (interface{}, *GoLib.ErrorResponse) { json.Unmarshal(body, &data) // TODO Filters - (*resource.GetStore(resourceName)).CreateResources(resourceName, []map[string]interface{}{data}) + (*resource.GetProcessor().GetStore(resourceName)).CreateResources(resourceName, []map[string]interface{}{data}) return nil, nil } diff --git a/server/facade.go b/server/facade.go index 15a4f63..46e10ab 100644 --- a/server/facade.go +++ b/server/facade.go @@ -5,7 +5,6 @@ import ( "log" "net/http" - "github.com/ResourceAPI/Core/config" "github.com/ResourceAPI/REST/nodes" "github.com/Vilsol/GoLib" "github.com/gorilla/handlers" @@ -41,8 +40,9 @@ func (facade *RESTFacade) Initialize() error { // Start the facade. Must be a blocking call. func (facade *RESTFacade) Start() error { - fmt.Printf("REST server listening on port %d\n", config.Get().Port) - log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", config.Get().Port), facade.router)) + // TODO Per-Plugin Configs + fmt.Printf("REST server listening on port %d\n", 5020) + log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", 5020), facade.router)) return nil } From e3c12c39817ae40f20d1b1b3350b513ae3d0f1d2 Mon Sep 17 00:00:00 2001 From: Vilsol Date: Sun, 14 Oct 2018 09:13:37 +0300 Subject: [PATCH 03/10] Add License, update Readme --- LICENSE | 201 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 201 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 From 6630ca55b7262f97ab83af16513406376cb25b14 Mon Sep 17 00:00:00 2001 From: Vilsol Date: Wed, 17 Oct 2018 22:15:58 +0300 Subject: [PATCH 04/10] Refactor to StratoAPI --- LICENSE | 2 +- README.md | 2 +- go.mod | 6 +++--- main.go | 4 ++-- nodes/resource.go | 4 ++-- server/facade.go | 2 +- 6 files changed, 10 insertions(+), 10 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 cbfa6a8..9610bff 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ # REST -The REST API module for ResourceAPI/Core +The REST API module for StratoAPI/Core diff --git a/go.mod b/go.mod index df61869..f912439 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,9 @@ -module github.com/ResourceAPI/REST +module github.com/StratoAPI/REST -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/Vilsol/GoLib v0.0.7 github.com/gorilla/handlers v1.4.0 github.com/gorilla/mux v1.6.2 diff --git a/main.go b/main.go index 7951155..3992136 100644 --- a/main.go +++ b/main.go @@ -1,8 +1,8 @@ package main import ( - "github.com/ResourceAPI/Interface/plugins" - "github.com/ResourceAPI/REST/server" + "github.com/StratoAPI/Interface/plugins" + "github.com/StratoAPI/REST/server" ) type RESTPlugin string diff --git a/nodes/resource.go b/nodes/resource.go index c4dc6eb..ebfdc5e 100644 --- a/nodes/resource.go +++ b/nodes/resource.go @@ -5,8 +5,8 @@ import ( "io/ioutil" "net/http" - "github.com/ResourceAPI/Interface/resource" - "github.com/ResourceAPI/Interface/schema" + "github.com/StratoAPI/Interface/resource" + "github.com/StratoAPI/Interface/schema" "github.com/Vilsol/GoLib" "github.com/gorilla/mux" ) diff --git a/server/facade.go b/server/facade.go index 46e10ab..c4739e2 100644 --- a/server/facade.go +++ b/server/facade.go @@ -5,7 +5,7 @@ import ( "log" "net/http" - "github.com/ResourceAPI/REST/nodes" + "github.com/StratoAPI/REST/nodes" "github.com/Vilsol/GoLib" "github.com/gorilla/handlers" "github.com/gorilla/mux" From 7ae3833b26914d88c15d5f50047cee408318f72e Mon Sep 17 00:00:00 2001 From: Vilsol Date: Mon, 22 Oct 2018 03:42:08 +0300 Subject: [PATCH 05/10] Add filtering, add simple filter --- Makefile | 4 ++ go.mod | 6 +-- go.sum | 7 +++- nodes/resource.go | 102 ++++++++++++++++++++++++++++++++++++++++------ nodes/types.go | 7 +++- 5 files changed, 107 insertions(+), 19 deletions(-) diff --git a/Makefile b/Makefile index 8eae8b1..3fb7856 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ GOCMD=go GOBUILD=$(GOCMD) build +GOMOD=$(GOCMD) mod BINARY_NAME=rest.so all: build @@ -7,5 +8,8 @@ all: build build: $(GOBUILD) -buildmode=plugin -o $(BINARY_NAME) main.go +tidy: + $(GOMOD) tidy + clean: rm -f $(BINARY_NAME) \ No newline at end of file diff --git a/go.mod b/go.mod index f912439..94baa9a 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,8 @@ module github.com/StratoAPI/REST -replace github.com/StratoAPI/Interface v0.0.0 => ../Interface - require ( - github.com/StratoAPI/Interface v0.0.0 - github.com/Vilsol/GoLib v0.0.7 + github.com/StratoAPI/Interface v0.0.6 + github.com/Vilsol/GoLib v0.0.8 github.com/gorilla/handlers v1.4.0 github.com/gorilla/mux v1.6.2 ) diff --git a/go.sum b/go.sum index 1d1f1dc..0be7423 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,8 @@ -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/StratoAPI/Interface v0.0.6 h1:yZ3mzdy0zLDZKnXdpoD0jaJmBGaBOtT1nRedujW4O0s= +github.com/StratoAPI/Interface v0.0.6/go.mod h1:jLlD9bBfAuFE0VN6FkrvX3cMhuDnbikzonCMprOLsyw= +github.com/Vilsol/GoLib v0.0.8 h1:jU4uDxstMbiyPQSMKUKpYNjSw8XnwYMDsR9wYTQcaR4= +github.com/Vilsol/GoLib v0.0.8/go.mod h1:6c39n5wTtt5OSt8vEF1mlnc/lC+Xl0SlHG3vboC2pEQ= +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= diff --git a/nodes/resource.go b/nodes/resource.go index ebfdc5e..d14394c 100644 --- a/nodes/resource.go +++ b/nodes/resource.go @@ -5,8 +5,10 @@ import ( "io/ioutil" "net/http" + "github.com/StratoAPI/Interface/filter" "github.com/StratoAPI/Interface/resource" "github.com/StratoAPI/Interface/schema" + "github.com/Vilsol/GoLib" "github.com/gorilla/mux" ) @@ -24,8 +26,20 @@ func getResource(r *http.Request) (interface{}, *GoLib.ErrorResponse) { return nil, &ErrorResourceDoesNotExist } - // TODO Filters - resources, _ := (*resource.GetProcessor().GetStore(resourceName)).GetResources(resourceName, nil) + resultFilters, err := processFilters(r.URL.Query()["filters"]) + + if err != nil { + return nil, err + } + + resources, errG := resource.GetProcessor().GetResources(resourceName, resultFilters) + + if errG != nil { + resp := ErrorFetchingResource + resp.Message += errG.Error() + return nil, &resp + } + return resources, nil } @@ -41,19 +55,38 @@ func storeResource(r *http.Request) (interface{}, *GoLib.ErrorResponse) { return nil, &ErrorCouldNotReadBody } - valid, err := schema.GetProcessor().ResourceValid(resourceName, string(body)) + finalResources := make([]map[string]interface{}, 0) - if !valid { - resp := ErrorResourceInvalid - resp.Message += err.Error() - return nil, &resp - } + if body[0] == '[' { + var data []map[string]interface{} + json.Unmarshal(body, &data) - var data map[string]interface{} - json.Unmarshal(body, &data) + for _, d := range data { + valid, err := schema.GetProcessor().ResourceValidGo(resourceName, d) - // TODO Filters - (*resource.GetProcessor().GetStore(resourceName)).CreateResources(resourceName, []map[string]interface{}{data}) + if !valid { + resp := ErrorResourceInvalid + resp.Message += err.Error() + return nil, &resp + } + + finalResources = append(finalResources, d) + } + } else { + valid, err := schema.GetProcessor().ResourceValid(resourceName, string(body)) + + if !valid { + resp := ErrorResourceInvalid + resp.Message += err.Error() + return nil, &resp + } + + var data map[string]interface{} + json.Unmarshal(body, &data) + finalResources = append(finalResources, data) + } + + (*resource.GetProcessor().GetStore(resourceName)).CreateResources(resourceName, finalResources) return nil, nil } @@ -61,3 +94,48 @@ func storeResource(r *http.Request) (interface{}, *GoLib.ErrorResponse) { func deleteResource(_ *http.Request) (interface{}, *GoLib.ErrorResponse) { return nil, nil } + +func processFilters(filters []string) ([]filter.ProcessedFilter, *GoLib.ErrorResponse) { + resultFilters := make([]filter.ProcessedFilter, 0) + for _, f := range filters { + var objFilter filter.EncodedFilter + err := json.Unmarshal([]byte(f), &objFilter) + if err != nil { + resp := ErrorFilterInvalid + resp.Message += err.Error() + return nil, &resp + } + + if !filter.GetProcessor().FilterExists(objFilter.Type) { + return nil, &ErrorFilterDoesntExist + } + + filterData := filter.GetProcessor().CreateFilter(objFilter.Type) + err = json.Unmarshal(objFilter.Data, &filterData) + + if err != nil { + resp := ErrorFilterInvalid + resp.Message += err.Error() + return nil, &resp + } + + processedFilter := filter.ProcessedFilter{ + Type: objFilter.Type, + Data: filterData, + } + + valid, err := filter.GetProcessor().ValidateFilter(processedFilter) + + if !valid { + resp := ErrorFilterInvalid + if err != nil { + resp.Message += err.Error() + } + return nil, &resp + } + + resultFilters = append(resultFilters, processedFilter) + } + + return resultFilters, nil +} diff --git a/nodes/types.go b/nodes/types.go index 3b4e63a..e8824a3 100644 --- a/nodes/types.go +++ b/nodes/types.go @@ -1,9 +1,14 @@ package nodes -import "github.com/Vilsol/GoLib" +import ( + "github.com/Vilsol/GoLib" +) var ( ErrorCouldNotReadBody = GoLib.ErrorResponse{Code: 1, Message: "could not read body of request", Status: 400} ErrorResourceDoesNotExist = GoLib.ErrorResponse{Code: 2, Message: "resource does not exist", Status: 404} ErrorResourceInvalid = GoLib.ErrorResponse{Code: 3, Message: "resource does not meet schema: ", Status: 400} + ErrorFilterInvalid = GoLib.ErrorResponse{Code: 4, Message: "filter is not valid: ", Status: 400} + ErrorFilterDoesntExist = GoLib.ErrorResponse{Code: 5, Message: "filter type does not exist", Status: 400} + ErrorFetchingResource = GoLib.ErrorResponse{Code: 6, Message: "error fetching resource: ", Status: 400} ) From 8b461a7b8a99cd05b55faf3a3c614cbb085a8562 Mon Sep 17 00:00:00 2001 From: Vilsol Date: Tue, 23 Oct 2018 04:49:53 +0300 Subject: [PATCH 06/10] Graceful shutdown, panic on timeout, check stores for existence --- server/facade.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/server/facade.go b/server/facade.go index c4739e2..4d19d15 100644 --- a/server/facade.go +++ b/server/facade.go @@ -1,8 +1,8 @@ package server import ( + "context" "fmt" - "log" "net/http" "github.com/StratoAPI/REST/nodes" @@ -13,6 +13,7 @@ import ( type RESTFacade struct { router http.Handler + server *http.Server } // Initialize the facade. @@ -35,6 +36,9 @@ func (facade *RESTFacade) Initialize() error { facade.router = finalRouter + // TODO Per-Plugin Configs + facade.server = &http.Server{Addr: fmt.Sprintf(":%d", 5020), Handler: facade.router} + return nil } @@ -42,11 +46,11 @@ func (facade *RESTFacade) Initialize() error { func (facade *RESTFacade) Start() error { // TODO Per-Plugin Configs fmt.Printf("REST server listening on port %d\n", 5020) - log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", 5020), facade.router)) + facade.server.ListenAndServe() return nil } // Graceful stopping of the facade with a 30s timeout. func (facade *RESTFacade) Stop() error { - return nil // TODO + return facade.server.Shutdown(context.Background()) } From 9cf1bcfe67ad0f37d189b5540e3a60234c8559c3 Mon Sep 17 00:00:00 2001 From: Vilsol Date: Tue, 23 Oct 2018 07:01:34 +0300 Subject: [PATCH 07/10] Complete REST and Flatfile-JSON implementations --- go.mod | 2 +- go.sum | 4 +- nodes/resource.go | 99 ++++++++++++++++++++++++++++++++++++++++++++--- nodes/types.go | 3 ++ 4 files changed, 99 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 94baa9a..8b83e6e 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,7 @@ module github.com/StratoAPI/REST require ( - github.com/StratoAPI/Interface v0.0.6 + github.com/StratoAPI/Interface v0.0.8 github.com/Vilsol/GoLib v0.0.8 github.com/gorilla/handlers v1.4.0 github.com/gorilla/mux v1.6.2 diff --git a/go.sum b/go.sum index 0be7423..9e72787 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -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/Vilsol/GoLib v0.0.8 h1:jU4uDxstMbiyPQSMKUKpYNjSw8XnwYMDsR9wYTQcaR4= github.com/Vilsol/GoLib v0.0.8/go.mod h1:6c39n5wTtt5OSt8vEF1mlnc/lC+Xl0SlHG3vboC2pEQ= github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f h1:9oNbS1z4rVpbnkHBdPZU4jo9bSmrLpII768arSyMFgk= diff --git a/nodes/resource.go b/nodes/resource.go index d14394c..63f436d 100644 --- a/nodes/resource.go +++ b/nodes/resource.go @@ -15,6 +15,7 @@ import ( func RegisterResourceRoutes(router GoLib.RegisterRoute) { router("GET", "/resource/{resource}", getResource) + router("PUT", "/resource/{resource}", updateResource) router("POST", "/resource/{resource}", storeResource) router("DELETE", "/resource/{resource}", deleteResource) } @@ -43,6 +44,52 @@ func getResource(r *http.Request) (interface{}, *GoLib.ErrorResponse) { return resources, nil } +func updateResource(r *http.Request) (interface{}, *GoLib.ErrorResponse) { + resourceName := mux.Vars(r)["resource"] + + if !schema.GetProcessor().ResourceExists(resourceName) { + return nil, &ErrorResourceDoesNotExist + } + + resultFilters, err := processFilters(r.URL.Query()["filters"]) + + if err != nil { + return nil, err + } + + body, errG := ioutil.ReadAll(r.Body) + if errG != nil { + return nil, &ErrorCouldNotReadBody + } + + valid, errG := schema.GetProcessor().ResourceValid(resourceName, string(body), false) + + if !valid { + resp := ErrorResourceInvalid + resp.Message += errG.Error() + return nil, &resp + } + + var data map[string]interface{} + errG = json.Unmarshal(body, &data) + + if errG != nil { + resp := ErrorResourceInvalid + resp.Message += errG.Error() + return nil, &resp + } + + errG = resource.GetProcessor().UpdateResources(resourceName, data, resultFilters) + + if errG != nil { + resp := ErrorUpdatingResource + resp.Message += errG.Error() + return nil, &resp + } + + return nil, nil +} + func storeResource(r *http.Request) (interface{}, *GoLib.ErrorResponse) { resourceName := mux.Vars(r)["resource"] @@ -59,10 +106,16 @@ func storeResource(r *http.Request) (interface{}, *GoLib.ErrorResponse) { if body[0] == '[' { var data []map[string]interface{} - json.Unmarshal(body, &data) + err := json.Unmarshal(body, &data) + + if err != nil { + resp := ErrorResourceInvalid + resp.Message += err.Error() + return nil, &resp + } for _, d := range data { - valid, err := schema.GetProcessor().ResourceValidGo(resourceName, d) + valid, err := schema.GetProcessor().ResourceValidGo(resourceName, d, true) if !valid { resp := ErrorResourceInvalid @@ -73,7 +126,7 @@ func storeResource(r *http.Request) (interface{}, *GoLib.ErrorResponse) { finalResources = append(finalResources, d) } } else { - valid, err := schema.GetProcessor().ResourceValid(resourceName, string(body)) + valid, err := schema.GetProcessor().ResourceValid(resourceName, string(body), true) if !valid { resp := ErrorResourceInvalid @@ -82,16 +135,50 @@ func storeResource(r *http.Request) (interface{}, *GoLib.ErrorResponse) { } var data map[string]interface{} - json.Unmarshal(body, &data) + + err = json.Unmarshal(body, &data) + + if err != nil { + resp := ErrorResourceInvalid + resp.Message += err.Error() + return nil, &resp + } + finalResources = append(finalResources, data) } - (*resource.GetProcessor().GetStore(resourceName)).CreateResources(resourceName, finalResources) + errG := (*resource.GetProcessor().GetStore(resourceName)).CreateResources(resourceName, finalResources) + + if errG != nil { + resp := ErrorCreatingResource + resp.Message += errG.Error() + return nil, &resp + } return nil, nil } -func deleteResource(_ *http.Request) (interface{}, *GoLib.ErrorResponse) { +func deleteResource(r *http.Request) (interface{}, *GoLib.ErrorResponse) { + resourceName := mux.Vars(r)["resource"] + + if !schema.GetProcessor().ResourceExists(resourceName) { + return nil, &ErrorResourceDoesNotExist + } + + resultFilters, err := processFilters(r.URL.Query()["filters"]) + + if err != nil { + return nil, err + } + + errG := resource.GetProcessor().DeleteResources(resourceName, resultFilters) + + if errG != nil { + resp := ErrorDeletingResource + resp.Message += errG.Error() + return nil, &resp + } + return nil, nil } diff --git a/nodes/types.go b/nodes/types.go index e8824a3..7e0908a 100644 --- a/nodes/types.go +++ b/nodes/types.go @@ -11,4 +11,7 @@ var ( ErrorFilterInvalid = GoLib.ErrorResponse{Code: 4, Message: "filter is not valid: ", Status: 400} ErrorFilterDoesntExist = GoLib.ErrorResponse{Code: 5, Message: "filter type does not exist", Status: 400} ErrorFetchingResource = GoLib.ErrorResponse{Code: 6, Message: "error fetching resource: ", Status: 400} + ErrorDeletingResource = GoLib.ErrorResponse{Code: 7, Message: "error deleting resource: ", Status: 400} + ErrorCreatingResource = GoLib.ErrorResponse{Code: 8, Message: "error creating resource: ", Status: 400} + ErrorUpdatingResource = GoLib.ErrorResponse{Code: 9, Message: "error updating resource: ", Status: 400} ) From 070b3004d3b1402d24d9234ede57b10324c346d3 Mon Sep 17 00:00:00 2001 From: Vilsol Date: Sun, 28 Oct 2018 19:41:25 +0200 Subject: [PATCH 08/10] Switch to echo http framework --- go.mod | 15 ++++- go.sum | 33 +++++++--- nodes/resource.go | 155 +++++++++++++++++++++------------------------- nodes/types.go | 47 ++++++++++---- server/facade.go | 52 ++++++++++------ 5 files changed, 175 insertions(+), 127 deletions(-) diff --git a/go.mod b/go.mod index 8b83e6e..cee1815 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,16 @@ module github.com/StratoAPI/REST require ( github.com/StratoAPI/Interface v0.0.8 - github.com/Vilsol/GoLib v0.0.8 - github.com/gorilla/handlers v1.4.0 - github.com/gorilla/mux v1.6.2 + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect + github.com/labstack/echo v3.2.1+incompatible + github.com/labstack/gommon v0.2.7 // indirect + github.com/mattn/go-colorable v0.0.9 // indirect + github.com/mattn/go-isatty v0.0.4 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/testify v1.2.2 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4 // indirect + golang.org/x/crypto v0.0.0-20181025213731-e84da0312774 // indirect + golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5 // indirect ) diff --git a/go.sum b/go.sum index 9e72787..f4d1606 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,26 @@ github.com/StratoAPI/Interface v0.0.8 h1:iOh/54yilmULpuS1t849SU91HTlLYyt5Dc1a8rFb5Bg= github.com/StratoAPI/Interface v0.0.8/go.mod h1:jLlD9bBfAuFE0VN6FkrvX3cMhuDnbikzonCMprOLsyw= -github.com/Vilsol/GoLib v0.0.8 h1:jU4uDxstMbiyPQSMKUKpYNjSw8XnwYMDsR9wYTQcaR4= -github.com/Vilsol/GoLib v0.0.8/go.mod h1:6c39n5wTtt5OSt8vEF1mlnc/lC+Xl0SlHG3vboC2pEQ= -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/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/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/labstack/echo v3.2.1+incompatible h1:J2M7YArHx4gi8p/3fDw8tX19SXhBCoRpviyAZSN3I88= +github.com/labstack/echo v3.2.1+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s= +github.com/labstack/gommon v0.2.7 h1:2qOPq/twXDrQ6ooBGrn3mrmVOC+biLlatwgIu8lbzRM= +github.com/labstack/gommon v0.2.7/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4= +github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +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= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4 h1:gKMu1Bf6QINDnvyZuTaACm9ofY+PRh+5vFz4oxBZeF8= +github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4/go.mod h1:50wTf68f99/Zt14pr046Tgt3Lp2vLyFZKzbFXTOabXw= +golang.org/x/crypto v0.0.0-20181025213731-e84da0312774 h1:a4tQYYYuK9QdeO/+kEvNYyuR21S+7ve5EANok6hABhI= +golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5 h1:x6r4Jo0KNzOOzYd8lbcRsqjuqEASK6ob3auvWYM4/8U= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/nodes/resource.go b/nodes/resource.go index 63f436d..9793c68 100644 --- a/nodes/resource.go +++ b/nodes/resource.go @@ -2,35 +2,31 @@ package nodes import ( "encoding/json" - "io/ioutil" - "net/http" - + "fmt" "github.com/StratoAPI/Interface/filter" "github.com/StratoAPI/Interface/resource" "github.com/StratoAPI/Interface/schema" - - "github.com/Vilsol/GoLib" - "github.com/gorilla/mux" + "github.com/labstack/echo" ) -func RegisterResourceRoutes(router GoLib.RegisterRoute) { - router("GET", "/resource/{resource}", getResource) - router("PUT", "/resource/{resource}", updateResource) - router("POST", "/resource/{resource}", storeResource) - router("DELETE", "/resource/{resource}", deleteResource) +func RegisterResourceRoutes(router *echo.Group) { + router.GET("/resource/:resource", getResource) + router.PUT("/resource/:resource", updateResource) + router.POST("/resource/:resource", storeResource) + router.DELETE("/resource/:resource", deleteResource) } -func getResource(r *http.Request) (interface{}, *GoLib.ErrorResponse) { - resourceName := mux.Vars(r)["resource"] +func getResource(c echo.Context) error { + resourceName := c.Param("resource") if !schema.GetProcessor().ResourceExists(resourceName) { - return nil, &ErrorResourceDoesNotExist + return PrepError(c, ErrorResourceDoesNotExist) } - resultFilters, err := processFilters(r.URL.Query()["filters"]) + resultFilters, err := processFilters(c.QueryParams()["filters"]) if err != nil { - return nil, err + return PrepError(c, *err) } resources, errG := resource.GetProcessor().GetResources(resourceName, resultFilters) @@ -38,137 +34,128 @@ func getResource(r *http.Request) (interface{}, *GoLib.ErrorResponse) { if errG != nil { resp := ErrorFetchingResource resp.Message += errG.Error() - return nil, &resp + return PrepError(c, resp) } - return resources, nil + return c.JSON(200, ResponseResource{ + Success: true, + Data: &resources, + }) } -func updateResource(r *http.Request) (interface{}, *GoLib.ErrorResponse) { - resourceName := mux.Vars(r)["resource"] +func updateResource(c echo.Context) error { + resourceName := c.Param("resource") if !schema.GetProcessor().ResourceExists(resourceName) { - return nil, &ErrorResourceDoesNotExist + return PrepError(c, ErrorResourceDoesNotExist) } - resultFilters, err := processFilters(r.URL.Query()["filters"]) + resultFilters, err := processFilters(c.QueryParams()["filters"]) if err != nil { - return nil, err + return PrepError(c, *err) } - body, errG := ioutil.ReadAll(r.Body) + var rawData map[string]interface{} + errG := c.Bind(&rawData) + if errG != nil { - return nil, &ErrorCouldNotReadBody + resp := ErrorCouldNotReadBody + resp.Message += fmt.Sprintf("%s", errG.(*echo.HTTPError).Message) + return PrepError(c, resp) } - valid, errG := schema.GetProcessor().ResourceValid(resourceName, string(body), false) + valid, errG := schema.GetProcessor().ResourceValidGo(resourceName, rawData, false) if !valid { resp := ErrorResourceInvalid resp.Message += errG.Error() - return nil, &resp - } - - var data map[string]interface{} - errG = json.Unmarshal(body, &data) - - if errG != nil { - resp := ErrorResourceInvalid - resp.Message += errG.Error() - return nil, &resp + return PrepError(c, resp) } - errG = resource.GetProcessor().UpdateResources(resourceName, data, resultFilters) + errG = resource.GetProcessor().UpdateResources(resourceName, rawData, resultFilters) if errG != nil { resp := ErrorUpdatingResource resp.Message += errG.Error() - return nil, &resp + return PrepError(c, resp) } - return nil, nil + return c.JSON(200, Response{ + Success: true, + }) } -func storeResource(r *http.Request) (interface{}, *GoLib.ErrorResponse) { - resourceName := mux.Vars(r)["resource"] +func storeResource(c echo.Context) error { + resourceName := c.Param("resource") if !schema.GetProcessor().ResourceExists(resourceName) { - return nil, &ErrorResourceDoesNotExist + return PrepError(c, ErrorResourceDoesNotExist) } - body, err := ioutil.ReadAll(r.Body) + var rawData interface{} + err := c.Bind(&rawData) + if err != nil { - return nil, &ErrorCouldNotReadBody + resp := ErrorCouldNotReadBody + resp.Message += fmt.Sprintf("%s", err.(*echo.HTTPError).Message) + return PrepError(c, resp) } finalResources := make([]map[string]interface{}, 0) - if body[0] == '[' { - var data []map[string]interface{} - err := json.Unmarshal(body, &data) - - if err != nil { - resp := ErrorResourceInvalid - resp.Message += err.Error() - return nil, &resp - } - - for _, d := range data { + if arrData, ok := rawData.([]map[string]interface{}); ok { + for _, d := range arrData { valid, err := schema.GetProcessor().ResourceValidGo(resourceName, d, true) if !valid { resp := ErrorResourceInvalid resp.Message += err.Error() - return nil, &resp + return PrepError(c, resp) } finalResources = append(finalResources, d) } - } else { - valid, err := schema.GetProcessor().ResourceValid(resourceName, string(body), true) + } else if singleData, ok := rawData.(map[string]interface{}); ok { + valid, err := schema.GetProcessor().ResourceValidGo(resourceName, singleData, true) if !valid { resp := ErrorResourceInvalid resp.Message += err.Error() - return nil, &resp + return PrepError(c, resp) } - var data map[string]interface{} - - err = json.Unmarshal(body, &data) - - if err != nil { - resp := ErrorResourceInvalid - resp.Message += err.Error() - return nil, &resp - } - - finalResources = append(finalResources, data) + finalResources = append(finalResources, singleData) + } else { + resp := ErrorCouldNotReadBody + resp.Message += "not of type object or array of object" + return PrepError(c, resp) } - errG := (*resource.GetProcessor().GetStore(resourceName)).CreateResources(resourceName, finalResources) + err = (*resource.GetProcessor().GetStore(resourceName)).CreateResources(resourceName, finalResources) - if errG != nil { + if err != nil { resp := ErrorCreatingResource - resp.Message += errG.Error() - return nil, &resp + resp.Message += err.Error() + return PrepError(c, resp) } - return nil, nil + return c.JSON(200, Response{ + Success: true, + }) } -func deleteResource(r *http.Request) (interface{}, *GoLib.ErrorResponse) { - resourceName := mux.Vars(r)["resource"] +func deleteResource(c echo.Context) error { + resourceName := c.Param("resource") if !schema.GetProcessor().ResourceExists(resourceName) { - return nil, &ErrorResourceDoesNotExist + return PrepError(c, ErrorResourceDoesNotExist) } - resultFilters, err := processFilters(r.URL.Query()["filters"]) + resultFilters, err := processFilters(c.QueryParams()["filters"]) if err != nil { - return nil, err + return PrepError(c, *err) } errG := resource.GetProcessor().DeleteResources(resourceName, resultFilters) @@ -176,13 +163,15 @@ func deleteResource(r *http.Request) (interface{}, *GoLib.ErrorResponse) { if errG != nil { resp := ErrorDeletingResource resp.Message += errG.Error() - return nil, &resp + return PrepError(c, resp) } - return nil, nil + return c.JSON(200, Response{ + Success: true, + }) } -func processFilters(filters []string) ([]filter.ProcessedFilter, *GoLib.ErrorResponse) { +func processFilters(filters []string) ([]filter.ProcessedFilter, *Error) { resultFilters := make([]filter.ProcessedFilter, 0) for _, f := range filters { var objFilter filter.EncodedFilter diff --git a/nodes/types.go b/nodes/types.go index 7e0908a..9015fac 100644 --- a/nodes/types.go +++ b/nodes/types.go @@ -1,17 +1,40 @@ package nodes -import ( - "github.com/Vilsol/GoLib" -) +import "github.com/labstack/echo" + +type Response struct { + Success bool `json:"success"` + Data *interface{} `json:"data,omitempty"` + Error *Error `json:"error,omitempty"` +} + +type ResponseResource struct { + Success bool `json:"success"` + Data *[]map[string]interface{} `json:"data"` + Error *Error `json:"error,omitempty"` +} + +type Error struct { + Code int `json:"code"` + Message string `json:"message"` + Status int `json:"-"` +} var ( - ErrorCouldNotReadBody = GoLib.ErrorResponse{Code: 1, Message: "could not read body of request", Status: 400} - ErrorResourceDoesNotExist = GoLib.ErrorResponse{Code: 2, Message: "resource does not exist", Status: 404} - ErrorResourceInvalid = GoLib.ErrorResponse{Code: 3, Message: "resource does not meet schema: ", Status: 400} - ErrorFilterInvalid = GoLib.ErrorResponse{Code: 4, Message: "filter is not valid: ", Status: 400} - ErrorFilterDoesntExist = GoLib.ErrorResponse{Code: 5, Message: "filter type does not exist", Status: 400} - ErrorFetchingResource = GoLib.ErrorResponse{Code: 6, Message: "error fetching resource: ", Status: 400} - ErrorDeletingResource = GoLib.ErrorResponse{Code: 7, Message: "error deleting resource: ", Status: 400} - ErrorCreatingResource = GoLib.ErrorResponse{Code: 8, Message: "error creating resource: ", Status: 400} - ErrorUpdatingResource = GoLib.ErrorResponse{Code: 9, Message: "error updating resource: ", Status: 400} + ErrorCouldNotReadBody = Error{Code: 1, Message: "could not read body of request: ", Status: 400} + ErrorResourceDoesNotExist = Error{Code: 2, Message: "resource does not exist", Status: 404} + ErrorResourceInvalid = Error{Code: 3, Message: "resource does not meet schema: ", Status: 400} + ErrorFilterInvalid = Error{Code: 4, Message: "filter is not valid: ", Status: 400} + ErrorFilterDoesntExist = Error{Code: 5, Message: "filter type does not exist", Status: 400} + ErrorFetchingResource = Error{Code: 6, Message: "error fetching resource: ", Status: 400} + ErrorDeletingResource = Error{Code: 7, Message: "error deleting resource: ", Status: 400} + ErrorCreatingResource = Error{Code: 8, Message: "error creating resource: ", Status: 400} + ErrorUpdatingResource = Error{Code: 9, Message: "error updating resource: ", Status: 400} ) + +func PrepError(c echo.Context, err Error) error { + return c.JSON(err.Status, Response{ + Success: false, + Error: &err, + }) +} diff --git a/server/facade.go b/server/facade.go index 4d19d15..5ac7958 100644 --- a/server/facade.go +++ b/server/facade.go @@ -6,51 +6,63 @@ import ( "net/http" "github.com/StratoAPI/REST/nodes" - "github.com/Vilsol/GoLib" - "github.com/gorilla/handlers" - "github.com/gorilla/mux" + "github.com/labstack/echo" + "github.com/labstack/echo/middleware" ) type RESTFacade struct { + echo *echo.Echo router http.Handler server *http.Server } // Initialize the facade. func (facade *RESTFacade) Initialize() error { - router := mux.NewRouter() - router.NotFoundHandler = GoLib.LoggerHandler(GoLib.NotFoundHandler()) - v1 := GoLib.RouteHandler(router, "/v1") - nodes.RegisterResourceRoutes(v1) + e := echo.New() + e.HTTPErrorHandler = customHTTPErrorHandler - CORSHandler := handlers.CORS( - handlers.AllowedOrigins([]string{"*"}), - handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS"}), - ) + e.Pre(middleware.RemoveTrailingSlash()) - finalRouter := GoLib.LoggerHandler(router) - finalRouter = handlers.CompressHandler(finalRouter) - finalRouter = handlers.ProxyHeaders(finalRouter) - finalRouter = CORSHandler(finalRouter) + nodes.RegisterResourceRoutes(e.Group("/v1")) - facade.router = finalRouter + e.Use(middleware.Recover()) + e.Use(middleware.CORS()) + e.Use(middleware.Gzip()) + e.Use(middleware.Logger()) - // TODO Per-Plugin Configs - facade.server = &http.Server{Addr: fmt.Sprintf(":%d", 5020), Handler: facade.router} + facade.echo = e return nil } +func customHTTPErrorHandler(err error, c echo.Context) { + code := http.StatusInternalServerError + message := err.Error() + + if he, ok := err.(*echo.HTTPError); ok { + code = he.Code + message = fmt.Sprintf("%s", he.Message) + } + + c.JSON(code, nodes.Response{ + Success: false, + Error: &nodes.Error{ + Code: -1, + Message: message, + }, + }) +} + // Start the facade. Must be a blocking call. func (facade *RESTFacade) Start() error { // TODO Per-Plugin Configs fmt.Printf("REST server listening on port %d\n", 5020) - facade.server.ListenAndServe() + facade.echo.Start(fmt.Sprintf(":%d", 5020)) return nil } // Graceful stopping of the facade with a 30s timeout. func (facade *RESTFacade) Stop() error { - return facade.server.Shutdown(context.Background()) + return facade.echo.Shutdown(context.Background()) } From 2d3e34976442f5ec8128414b32b4757524458294 Mon Sep 17 00:00:00 2001 From: Vilsol Date: Sun, 28 Oct 2018 22:57:20 +0200 Subject: [PATCH 09/10] Add middleware support --- go.mod | 2 +- go.sum | 4 ++-- nodes/resource.go | 46 ++++++++++++++++++++++++++++++++++++++++++++++ nodes/types.go | 1 + 4 files changed, 50 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index cee1815..60f7870 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,7 @@ module github.com/StratoAPI/REST 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/dgrijalva/jwt-go v3.2.0+incompatible // indirect github.com/labstack/echo v3.2.1+incompatible diff --git a/go.sum b/go.sum index f4d1606..bb2eb91 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/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= diff --git a/nodes/resource.go b/nodes/resource.go index 9793c68..ab8963f 100644 --- a/nodes/resource.go +++ b/nodes/resource.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "github.com/StratoAPI/Interface/filter" + "github.com/StratoAPI/Interface/middleware" "github.com/StratoAPI/Interface/resource" "github.com/StratoAPI/Interface/schema" "github.com/labstack/echo" @@ -23,6 +24,15 @@ func getResource(c echo.Context) error { return PrepError(c, ErrorResourceDoesNotExist) } + request := middleware.GetProcessor().Request(resourceName, c.Request().Header) + + if request != nil { + resp := ErrorMiddleware + resp.Message += request.Message + resp.Status = request.Code + return PrepError(c, resp) + } + resultFilters, err := processFilters(c.QueryParams()["filters"]) if err != nil { @@ -37,6 +47,15 @@ func getResource(c echo.Context) error { return PrepError(c, resp) } + resources, response := middleware.GetProcessor().Response(resourceName, c.Request().Header, resources) + + if response != nil { + resp := ErrorMiddleware + resp.Message += response.Message + resp.Status = response.Code + return PrepError(c, resp) + } + return c.JSON(200, ResponseResource{ Success: true, Data: &resources, @@ -50,6 +69,15 @@ func updateResource(c echo.Context) error { return PrepError(c, ErrorResourceDoesNotExist) } + request := middleware.GetProcessor().Request(resourceName, c.Request().Header) + + if request != nil { + resp := ErrorMiddleware + resp.Message += request.Message + resp.Status = request.Code + return PrepError(c, resp) + } + resultFilters, err := processFilters(c.QueryParams()["filters"]) if err != nil { @@ -93,6 +121,15 @@ func storeResource(c echo.Context) error { return PrepError(c, ErrorResourceDoesNotExist) } + request := middleware.GetProcessor().Request(resourceName, c.Request().Header) + + if request != nil { + resp := ErrorMiddleware + resp.Message += request.Message + resp.Status = request.Code + return PrepError(c, resp) + } + var rawData interface{} err := c.Bind(&rawData) @@ -152,6 +189,15 @@ func deleteResource(c echo.Context) error { return PrepError(c, ErrorResourceDoesNotExist) } + request := middleware.GetProcessor().Request(resourceName, c.Request().Header) + + if request != nil { + resp := ErrorMiddleware + resp.Message += request.Message + resp.Status = request.Code + return PrepError(c, resp) + } + resultFilters, err := processFilters(c.QueryParams()["filters"]) if err != nil { diff --git a/nodes/types.go b/nodes/types.go index 9015fac..1a5e0c5 100644 --- a/nodes/types.go +++ b/nodes/types.go @@ -30,6 +30,7 @@ var ( ErrorDeletingResource = Error{Code: 7, Message: "error deleting resource: ", Status: 400} ErrorCreatingResource = Error{Code: 8, Message: "error creating resource: ", Status: 400} ErrorUpdatingResource = Error{Code: 9, Message: "error updating resource: ", Status: 400} + ErrorMiddleware = Error{Code: 10, Message: "middleware error: ", Status: 400} ) func PrepError(c echo.Context, err Error) error { From 932a3f6dc3236897ba204834dca5f00a0554a7bf Mon Sep 17 00:00:00 2001 From: Vilsol Date: Tue, 30 Oct 2018 00:49:39 +0200 Subject: [PATCH 10/10] Custom registrable plugin configs --- config.example.json | 4 ++++ config/config.go | 27 +++++++++++++++++++++++++++ go.mod | 2 +- go.sum | 4 ++-- main.go | 2 ++ server/facade.go | 6 +++--- 6 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 config.example.json create mode 100644 config/config.go diff --git a/config.example.json b/config.example.json new file mode 100644 index 0000000..c5fc962 --- /dev/null +++ b/config.example.json @@ -0,0 +1,4 @@ +{ + "host": "0.0.0.0", + "port": 5020 +} \ No newline at end of file diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..19e2c73 --- /dev/null +++ b/config/config.go @@ -0,0 +1,27 @@ +package config + +type RESTConfigData struct { + Host string `json:"host"` + Port uint32 `json:"port"` +} + +type RESTConfig struct { + Config *RESTConfigData +} + +var restConfig = RESTConfig{} + +func (config *RESTConfig) CreateStructure() interface{} { + return &RESTConfigData{ + Host: "0.0.0.0", + Port: 5020, + } +} + +func (config *RESTConfig) Set(data interface{}) { + config.Config = data.(*RESTConfigData) +} + +func Get() *RESTConfig { + return &restConfig +} diff --git a/go.mod b/go.mod index 60f7870..433916b 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,7 @@ module github.com/StratoAPI/REST 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/dgrijalva/jwt-go v3.2.0+incompatible // indirect github.com/labstack/echo v3.2.1+incompatible diff --git a/go.sum b/go.sum index bb2eb91..ac3458f 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/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= diff --git a/main.go b/main.go index 3992136..68720d9 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( "github.com/StratoAPI/Interface/plugins" + "github.com/StratoAPI/REST/config" "github.com/StratoAPI/REST/server" ) @@ -13,6 +14,7 @@ func (RESTPlugin) Name() string { func (RESTPlugin) Entrypoint() { plugins.GetRegistry().RegisterFacade("REST", &server.RESTFacade{}) + plugins.GetRegistry().RegisterConfig("rest", config.Get()) } var CorePlugin RESTPlugin diff --git a/server/facade.go b/server/facade.go index 5ac7958..67ac41b 100644 --- a/server/facade.go +++ b/server/facade.go @@ -3,6 +3,7 @@ package server import ( "context" "fmt" + "github.com/StratoAPI/REST/config" "net/http" "github.com/StratoAPI/REST/nodes" @@ -56,9 +57,8 @@ func customHTTPErrorHandler(err error, c echo.Context) { // Start the facade. Must be a blocking call. func (facade *RESTFacade) Start() error { - // TODO Per-Plugin Configs - fmt.Printf("REST server listening on port %d\n", 5020) - facade.echo.Start(fmt.Sprintf(":%d", 5020)) + fmt.Printf("REST server listening on port %d\n", config.Get().Config.Port) + facade.echo.Start(fmt.Sprintf("%s:%d", config.Get().Config.Host, config.Get().Config.Port)) return nil }