diff --git a/cmd/apiServerMain/apiServerMain.go b/cmd/apiServerMain/apiServerMain.go index e3a8edaf..706693ee 100644 --- a/cmd/apiServerMain/apiServerMain.go +++ b/cmd/apiServerMain/apiServerMain.go @@ -22,28 +22,26 @@ import ( // @contact.url http://www.swagger.io/support // @contact.email support@swagger.io -// @license.name Apache 2.0 -// @license.url http://www.apache.org/licenses/LICENSE-2.0.html +// @license.name Apache 2.0 +// @license.url http://www.apache.org/licenses/LICENSE-2.0.html func main() { - log.Println("Reading config..") - config, err := config.Readconfig("config_app_secrets", "env") - if err != nil { - fmt.Printf("Error: cannot read config: %s", err) + if err != nil { + fmt.Printf("Error: cannot read config: %s", err) os.Exit(42) - } + } log.Printf("Config: %s", config.DBUser) log.Println("Starting up database..") dataBase, err := models.NewPostgresDb(config.DBUser, - config.DBName, - config.DBPassword, - config.DBSSLmode, - config.DBMaxOpenConns, - config.DBMaxOpenConns) + config.DBName, + config.DBPassword, + config.DBSSLmode, + config.DBMaxOpenConns, + config.DBMaxOpenConns) if err != nil { fmt.Printf("Error connecting to database: %s", err) os.Exit(42) @@ -55,9 +53,7 @@ func main() { } log.Println("Starting up server..") - port := fmt.Sprintf(":%s", config.APIPort) + port := fmt.Sprintf(":%s", config.APIPort) server := api.NewAPIServer(port, dataBase) server.Run(port) - } - diff --git a/cmd/paginateTest/paginateTest.go b/cmd/paginateTest/paginateTest.go index f8b66c89..33dcef47 100644 --- a/cmd/paginateTest/paginateTest.go +++ b/cmd/paginateTest/paginateTest.go @@ -8,10 +8,9 @@ import ( ) type test_struct struct { - Test string + Test string } - func main() { baseURL := "http://localhost:8969/" resource := "/services" @@ -33,8 +32,7 @@ func main() { urlStr := fmt.Sprintf("%v", u) log.Printf("Listing services by using limit & offset to %s", urlStr) - page = page + 1 + page = page + 1 } - } diff --git a/cmd/patheticTester/patheticTester.go b/cmd/patheticTester/patheticTester.go index 47bbaccd..197a5746 100644 --- a/cmd/patheticTester/patheticTester.go +++ b/cmd/patheticTester/patheticTester.go @@ -8,17 +8,19 @@ import ( ) // Declaring Global variables -var baseURL string = "http://localhost:8969/" -var testsPassed int = 0 -var testsFailed int = 0 +var ( + baseURL string = "http://localhost:8969/" + testsPassed int = 0 + testsFailed int = 0 +) func scoreGlobalTestsPassedandFailes(testCode, wantedCode int) { if testCode == wantedCode { color.Green("Test passed: %d", testCode) - testsPassed ++ + testsPassed++ } else { color.Red("Error!! Wanted %d, but got unexpected status: %d", wantedCode, testCode) - testsFailed ++ + testsFailed++ } } @@ -28,8 +30,6 @@ func printTestSeperator(testCategory string) { } func main() { - - // Run tests printTestSeperator("testGetHealthendpoint") testGetHealthendpoint() @@ -55,5 +55,4 @@ func main() { println("\n\n") color.Red("Tests failed: %s", strconv.Itoa(testsFailed)) color.Green("Tests passed: %s", strconv.Itoa(testsPassed)) - -} \ No newline at end of file +} diff --git a/cmd/patheticTester/testDeleteServiceById.go b/cmd/patheticTester/testDeleteServiceById.go index 52d6a310..4976b53c 100644 --- a/cmd/patheticTester/testDeleteServiceById.go +++ b/cmd/patheticTester/testDeleteServiceById.go @@ -6,8 +6,6 @@ import ( "github.com/stablecaps/services-api-go/internal/dbtools" ) - - func testDeleteserviceById() { postedServiceData := dbtools.CreateExplicitService(dbtools.MakeRandomName(), dbtools.MakeRandomDescription(4), "v1,v2,v3") @@ -18,13 +16,12 @@ func testDeleteserviceById() { paramMapList := map[string]string{} resp, _ := dbtools.MakeHttpRequestWrapper(baseURL, fullEndpoint, "DELETE", paramMapList, nil) -scoreGlobalTestsPassedandFailes(resp.StatusCode, 200) + scoreGlobalTestsPassedandFailes(resp.StatusCode, 200) } func testDeleteserviceByIdError() { endpoint := "/services/id/" - testNameSlice := []string{ // Test serviceId 400s "badServiceId", "outOfRangeServiceId", @@ -35,7 +32,6 @@ func testDeleteserviceByIdError() { paramMapList := map[string]string{} serviceIdList := []string{"fake", "-10", "99999999"} - for idx, testName := range testNameSlice { fmt.Println("\n~~~~~~~~~~~~~~~~~~~~") fmt.Printf("Running test %d: - %s\n", idx, testName) @@ -47,4 +43,4 @@ func testDeleteserviceByIdError() { scoreGlobalTestsPassedandFailes(resp.StatusCode, wantedCodes[idx]) } -} \ No newline at end of file +} diff --git a/cmd/patheticTester/testGetServiceById.go b/cmd/patheticTester/testGetServiceById.go index d4c20341..db9702ac 100644 --- a/cmd/patheticTester/testGetServiceById.go +++ b/cmd/patheticTester/testGetServiceById.go @@ -9,7 +9,6 @@ import ( func testGetServiceById() { endpoint := "/services/id/" - testNameSlice := []string{ // Test serviceId 400s "badServiceId", "outOfRangeServiceId", @@ -18,8 +17,7 @@ func testGetServiceById() { } wantedCodes := []int{404, 404, 200} paramMapList := map[string]string{} - serviceIdList := []string{"fake", "-10", "5",} - + serviceIdList := []string{"fake", "-10", "5"} for idx, testName := range testNameSlice { fmt.Println("\n~~~~~~~~~~~~~~~~~~~~") @@ -30,4 +28,4 @@ func testGetServiceById() { scoreGlobalTestsPassedandFailes(resp.StatusCode, wantedCodes[idx]) } -} \ No newline at end of file +} diff --git a/cmd/patheticTester/testGetServiceByName.go b/cmd/patheticTester/testGetServiceByName.go index d3215dca..26d56db3 100644 --- a/cmd/patheticTester/testGetServiceByName.go +++ b/cmd/patheticTester/testGetServiceByName.go @@ -21,7 +21,6 @@ func testGetServiceByName() { paramMapList := map[string]string{} serviceNameList := []string{"10", "NonExistantService", goodService.ServiceName} - for idx, testName := range testNameSlice { fmt.Println("\n~~~~~~~~~~~~~~~~~~~~") fmt.Printf("Running test %d: - %s\n", idx, testName) @@ -32,4 +31,4 @@ func testGetServiceByName() { scoreGlobalTestsPassedandFailes(resp.StatusCode, wantedCodes[idx]) } -} \ No newline at end of file +} diff --git a/cmd/patheticTester/testGetServiceVersionsById.go b/cmd/patheticTester/testGetServiceVersionsById.go index c1487086..96818843 100644 --- a/cmd/patheticTester/testGetServiceVersionsById.go +++ b/cmd/patheticTester/testGetServiceVersionsById.go @@ -23,7 +23,6 @@ func testGetServiceVersionsById() { paramMapList := map[string]string{} serviceIdList := []string{"9999999999999", strconv.Itoa(postedServiceData.ServiceId)} - for idx, testName := range testNameSlice { fmt.Println("\n~~~~~~~~~~~~~~~~~~~~") fmt.Printf("Running test %d: - %s\n", idx, testName) @@ -33,4 +32,4 @@ func testGetServiceVersionsById() { scoreGlobalTestsPassedandFailes(resp.StatusCode, wantedCodes[idx]) } -} \ No newline at end of file +} diff --git a/cmd/patheticTester/testHealthEndpoint.go b/cmd/patheticTester/testHealthEndpoint.go index 029154dc..1d4d0d7a 100644 --- a/cmd/patheticTester/testHealthEndpoint.go +++ b/cmd/patheticTester/testHealthEndpoint.go @@ -13,15 +13,13 @@ import ( func testGetHealthendpoint() { healthEndpoint := "/health" - testNameSlice := []string{ // Test health 200 "goodCall", } - wantedCodes := []int{200,} + wantedCodes := []int{200} paramMapList := map[string]string{} - for idx, testName := range testNameSlice { fmt.Println("\n~~~~~~~~~~~~~~~~~~~~") fmt.Printf("Running test %d: - %s\n", idx, testName) @@ -32,7 +30,6 @@ func testGetHealthendpoint() { } func TestHealthCheck(t *testing.T) { - response, err := http.Get("http://localhost:8969/health") if err != nil { t.Errorf("expected no errors, but got %v", err) diff --git a/cmd/patheticTester/testListService.go b/cmd/patheticTester/testListService.go index 2f1fa730..8aecb3b8 100644 --- a/cmd/patheticTester/testListService.go +++ b/cmd/patheticTester/testListService.go @@ -6,12 +6,10 @@ import ( "github.com/stablecaps/services-api-go/internal/dbtools" ) - func testListServices() { // list services pathetic tests endpoint := "/services" - testNameSlice := []string{ // Test 400s "badLimit", "outOfRangeLimit", "badOffset", "outOfRangeOffset", "badOrderColName", "badOrderDir", @@ -24,88 +22,87 @@ func testListServices() { paramMapList := []map[string]string{ // Test 400s { - "limit": "fake", - "offset": "0", + "limit": "fake", + "offset": "0", "orderColName": "serviceName", - "orderDir": "asc", + "orderDir": "asc", }, { - "limit": "-10", - "offset": "0", + "limit": "-10", + "offset": "0", "orderColName": "serviceName", - "orderDir": "asc", + "orderDir": "asc", }, { - "limit": "4", - "offset": "fake", + "limit": "4", + "offset": "fake", "orderColName": "serviceName", - "orderDir": "asc", + "orderDir": "asc", }, { - "limit": "4", - "offset": "-10", + "limit": "4", + "offset": "-10", "orderColName": "serviceName", - "orderDir": "asc", + "orderDir": "asc", }, { - "limit": "4", - "offset": "0", + "limit": "4", + "offset": "0", "orderColName": "fake", }, { - "limit": "4", - "offset": "0", + "limit": "4", + "offset": "0", "orderColName": "serviceName", - "orderDir": "fake", + "orderDir": "fake", }, // Test orderColName 200 { - "limit": "4", - "offset": "0", + "limit": "4", + "offset": "0", "orderColName": "serviceId", - "orderDir": "asc", + "orderDir": "asc", }, { - "limit": "4", - "offset": "0", + "limit": "4", + "offset": "0", "orderColName": "serviceName", - "orderDir": "asc", + "orderDir": "asc", }, { - "limit": "4", - "offset": "0", + "limit": "4", + "offset": "0", "orderColName": "serviceDescription", - "orderDir": "asc", + "orderDir": "asc", }, { - "limit": "4", - "offset": "0", + "limit": "4", + "offset": "0", "orderColName": "serviceVersions", - "orderDir": "asc", + "orderDir": "asc", }, { - "limit": "4", - "offset": "0", + "limit": "4", + "offset": "0", "orderColName": "createdAt", - "orderDir": "asc", + "orderDir": "asc", }, // Test orderDir 200 { - "limit": "4", - "offset": "0", + "limit": "4", + "offset": "0", "orderColName": "serviceName", - "orderDir": "asc", + "orderDir": "asc", }, { - "limit": "4", - "offset": "0", + "limit": "4", + "offset": "0", "orderColName": "serviceName", - "orderDir": "asc", + "orderDir": "asc", }, } - for idx, testName := range testNameSlice { fmt.Println("\n~~~~~~~~~~~~~~~~~~~~") fmt.Printf("Running test %d: - %s\n", idx, testName) diff --git a/cmd/populateDb/populateDb.go b/cmd/populateDb/populateDb.go index c7debec1..4b18887c 100644 --- a/cmd/populateDb/populateDb.go +++ b/cmd/populateDb/populateDb.go @@ -9,7 +9,6 @@ import ( // Declaring Global variables var baseURL string = "http://localhost:8969/" - func main() { endpoint := "/services/new" @@ -18,7 +17,6 @@ func main() { numServices := 500 paramMapList := map[string]string{} - for idx := 0; idx <= numServices; idx++ { log.Printf("Creating service no %d", idx) @@ -26,6 +24,4 @@ func main() { dbtools.MakeHttpRequestWrapper(baseURL, endpoint, "POST", paramMapList, body) } - } - diff --git a/internal/dbtools/dbtoolsCommon.go b/internal/dbtools/dbtoolsCommon.go index 16475da3..f3c653f2 100644 --- a/internal/dbtools/dbtoolsCommon.go +++ b/internal/dbtools/dbtoolsCommon.go @@ -29,25 +29,24 @@ func readHttpBody(resp *http.Response, httpVerb string) []byte { func MakeHttpRequestWrapper(baseURL, endpoint, httpVerb string, paramMap map[string]string, reqBody []byte) (*http.Response, []byte) { allowedHttpVerbs := []string{"GET", "POST", "DELETE"} - if ! slices.Contains(allowedHttpVerbs, httpVerb) { + if !slices.Contains(allowedHttpVerbs, httpVerb) { log.Printf("HTTP verb %s not allowed!", httpVerb) log.Printf("Must be one of %s", strings.Join(allowedHttpVerbs[:], ", ")) os.Exit(42) } - myUrl, _ := url.ParseRequestURI(baseURL) myUrl.Path = endpoint // add params if len(paramMap) > 0 { params := url.Values{} - for key, val := range paramMap { - fmt.Println(key, val) - params.Add(key, val) - } - myUrl.RawQuery = params.Encode() + for key, val := range paramMap { + fmt.Println(key, val) + params.Add(key, val) } + myUrl.RawQuery = params.Encode() + } myUrlStr := fmt.Sprintf("%v", myUrl) var req *http.Request @@ -77,7 +76,6 @@ func MakeHttpRequestWrapper(baseURL, endpoint, httpVerb string, paramMap map[str } func MakeExplicitServiceJson(name, description, versions string) []byte { - log.Printf("name is %s", name) log.Printf("description is %s", description) @@ -85,7 +83,7 @@ func MakeExplicitServiceJson(name, description, versions string) []byte { "serviceName": "%s", "serviceDescription": "%s", "serviceVersions": "%s" - }`, name, description, versions) ) + }`, name, description, versions)) return body } @@ -141,7 +139,7 @@ func CreateExplicitService(serviceName, serviceDescriptions, serviceVersions str fmt.Println("\n~~~~~~~~~~~~~~~~~~~~") fmt.Println("Posting sample service to test API delete endpoint") - //baseURL = "http://localhost:8969/" + // baseURL = "http://localhost:8969/" body := MakeExplicitServiceJson(serviceName, serviceDescriptions, serviceVersions) createNewServiceUrl := "http://localhost:8969/services/new" @@ -151,8 +149,6 @@ func CreateExplicitService(serviceName, serviceDescriptions, serviceVersions str } func PrettyPrint(i interface{}) string { - s, _ := json.MarshalIndent(i, "", "\t") - return string(s) + s, _ := json.MarshalIndent(i, "", "\t") + return string(s) } - - diff --git a/internal/dbtools/randomServiceCreators.go b/internal/dbtools/randomServiceCreators.go index 4dcc4d5b..3d819bc6 100644 --- a/internal/dbtools/randomServiceCreators.go +++ b/internal/dbtools/randomServiceCreators.go @@ -9,7 +9,6 @@ import ( "github.com/tjarratt/babble" ) - func MakeRandomName() string { babbler := babble.NewBabbler() babbler.Count = 1 @@ -36,7 +35,7 @@ func MakeRandomService() []byte { body := []byte(fmt.Sprintf(`{ "serviceName": "%s", "serviceDescription": "%s" - }`, randomName, radomDesc) ) + }`, randomName, radomDesc)) return body -} \ No newline at end of file +} diff --git a/pkg/api/apiErrorHandlers.go b/pkg/api/apiErrorHandlers.go index 85cda76b..5dc68432 100644 --- a/pkg/api/apiErrorHandlers.go +++ b/pkg/api/apiErrorHandlers.go @@ -12,105 +12,103 @@ import ( "github.com/golang/gddo/httputil/header" ) -//////////////////////////////////////////////////////// -//////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////// type malformedRequest struct { - status int - msg string + status int + msg string } func (mr *malformedRequest) Error() string { - return mr.msg + return mr.msg } func errorCheckJSONBody(writer http.ResponseWriter, req *http.Request, dst interface{}) error { - - // check correct content-type header is present - if req.Header.Get("Content-Type") != "" { - value, _ := header.ParseValueAndParams(req.Header, "Content-Type") - if value != "application/json" { - msg := "Content-Type header is not application/json" - return &malformedRequest{status: http.StatusUnsupportedMediaType, msg: msg} - } - } - - - // esure max 1 MB max payload in request body. A larget payload results in Decode() - // returning a "http: request body too large" error. - req.Body = http.MaxBytesReader(writer, req.Body, 1048576) - - // Setup the decoder and call the DisallowUnknownFields() method on it. - // This will cause Decode() to return a "json: unknown field ..." error - // if it encounters any extra unexpected fields in the JSON. Strictly - // speaking, it returns an error for "keys which do not match any - // non-ignored, exported fields in the destination". - dec := json.NewDecoder(req.Body) - dec.DisallowUnknownFields() - - err := dec.Decode(&dst) - if err != nil { - var syntaxError *json.SyntaxError - var unmarshalTypeError *json.UnmarshalTypeError - - switch { - // Catch any syntax errors in the JSON and send an error message - // which interpolates the location of the problem to make it - // easier for the client to fix. - case errors.As(err, &syntaxError): - msg := fmt.Sprintf("Request body contains badly-formed JSON (at position %d)", syntaxError.Offset) - return &malformedRequest{status: http.StatusBadRequest, msg: msg} - - // In some circumstances Decode() may also return an - // io.ErrUnexpectedEOF error for syntax errors in the JSON. - case errors.Is(err, io.ErrUnexpectedEOF): - msg := fmt.Sprintf("Request body contains badly-formed JSON") - return &malformedRequest{status: http.StatusBadRequest, msg: msg} - - // Catch any type errors, like trying to assign a string in the - // JSON request body to a int field in our Person struct. We can - // interpolate the relevant field name and position into the error - // message to make it easier for the client to fix. - case errors.As(err, &unmarshalTypeError): - msg := fmt.Sprintf("Request body contains an invalid value for the %q field (at position %d)", unmarshalTypeError.Field, unmarshalTypeError.Offset) - return &malformedRequest{status: http.StatusBadRequest, msg: msg} - - // Catch the error caused by extra unexpected fields in the request - // body. We extract the field name from the error message and - // interpolate it in our custom error message. - case strings.HasPrefix(err.Error(), "json: unknown field "): - fieldName := strings.TrimPrefix(err.Error(), "json: unknown field ") - msg := fmt.Sprintf("Request body contains unknown field %s", fieldName) - return &malformedRequest{status: http.StatusBadRequest, msg: msg} - - // An io.EOF error is returned by Decode() if the request body is - // empty. - case errors.Is(err, io.EOF): - msg := "Request body must not be empty" - return &malformedRequest{status: http.StatusBadRequest, msg: msg} - - // Catch the error caused by the request body being too large. - case err.Error() == "http: request body too large": - msg := "Request body must not be larger than 1MB" - return &malformedRequest{status: http.StatusRequestEntityTooLarge, msg: msg} - - // Otherwise default to logging the error and sending a 500 Internal - // Server Error response. - default: - log.Printf("Error default 500: %v", err.Error()) - msg := http.StatusText(http.StatusInternalServerError) - return &malformedRequest{status: http.StatusInternalServerError, msg: msg} - } - } - - // Call decode again, using a pointer to an empty anonymous struct as - // the destination. If the request body only contained a single JSON - // object this will return an io.EOF error. So if we get anything else, - // we know that there is additional data in the request body. + // check correct content-type header is present + if req.Header.Get("Content-Type") != "" { + value, _ := header.ParseValueAndParams(req.Header, "Content-Type") + if value != "application/json" { + msg := "Content-Type header is not application/json" + return &malformedRequest{status: http.StatusUnsupportedMediaType, msg: msg} + } + } + + // esure max 1 MB max payload in request body. A larget payload results in Decode() + // returning a "http: request body too large" error. + req.Body = http.MaxBytesReader(writer, req.Body, 1048576) + + // Setup the decoder and call the DisallowUnknownFields() method on it. + // This will cause Decode() to return a "json: unknown field ..." error + // if it encounters any extra unexpected fields in the JSON. Strictly + // speaking, it returns an error for "keys which do not match any + // non-ignored, exported fields in the destination". + dec := json.NewDecoder(req.Body) + dec.DisallowUnknownFields() + + err := dec.Decode(&dst) + if err != nil { + var syntaxError *json.SyntaxError + var unmarshalTypeError *json.UnmarshalTypeError + + switch { + // Catch any syntax errors in the JSON and send an error message + // which interpolates the location of the problem to make it + // easier for the client to fix. + case errors.As(err, &syntaxError): + msg := fmt.Sprintf("Request body contains badly-formed JSON (at position %d)", syntaxError.Offset) + return &malformedRequest{status: http.StatusBadRequest, msg: msg} + + // In some circumstances Decode() may also return an + // io.ErrUnexpectedEOF error for syntax errors in the JSON. + case errors.Is(err, io.ErrUnexpectedEOF): + msg := fmt.Sprintf("Request body contains badly-formed JSON") + return &malformedRequest{status: http.StatusBadRequest, msg: msg} + + // Catch any type errors, like trying to assign a string in the + // JSON request body to a int field in our Person struct. We can + // interpolate the relevant field name and position into the error + // message to make it easier for the client to fix. + case errors.As(err, &unmarshalTypeError): + msg := fmt.Sprintf("Request body contains an invalid value for the %q field (at position %d)", unmarshalTypeError.Field, unmarshalTypeError.Offset) + return &malformedRequest{status: http.StatusBadRequest, msg: msg} + + // Catch the error caused by extra unexpected fields in the request + // body. We extract the field name from the error message and + // interpolate it in our custom error message. + case strings.HasPrefix(err.Error(), "json: unknown field "): + fieldName := strings.TrimPrefix(err.Error(), "json: unknown field ") + msg := fmt.Sprintf("Request body contains unknown field %s", fieldName) + return &malformedRequest{status: http.StatusBadRequest, msg: msg} + + // An io.EOF error is returned by Decode() if the request body is + // empty. + case errors.Is(err, io.EOF): + msg := "Request body must not be empty" + return &malformedRequest{status: http.StatusBadRequest, msg: msg} + + // Catch the error caused by the request body being too large. + case err.Error() == "http: request body too large": + msg := "Request body must not be larger than 1MB" + return &malformedRequest{status: http.StatusRequestEntityTooLarge, msg: msg} + + // Otherwise default to logging the error and sending a 500 Internal + // Server Error response. + default: + log.Printf("Error default 500: %v", err.Error()) + msg := http.StatusText(http.StatusInternalServerError) + return &malformedRequest{status: http.StatusInternalServerError, msg: msg} + } + } + + // Call decode again, using a pointer to an empty anonymous struct as + // the destination. If the request body only contained a single JSON + // object this will return an io.EOF error. So if we get anything else, + // we know that there is additional data in the request body. err = dec.Decode(&struct{}{}) if !errors.Is(err, io.EOF) { - msg := "Request body must only contain a single JSON object" - return &malformedRequest{status: http.StatusBadRequest, msg: msg} - } + msg := "Request body must only contain a single JSON object" + return &malformedRequest{status: http.StatusBadRequest, msg: msg} + } - return nil + return nil } diff --git a/pkg/api/apiHandlers.go b/pkg/api/apiHandlers.go index 6bca3d36..be187a0b 100644 --- a/pkg/api/apiHandlers.go +++ b/pkg/api/apiHandlers.go @@ -20,8 +20,8 @@ type ApiError struct { Error string `json:"error"` } -//////////////////////////////////////////////////////// -//////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////// func WriteJson(writer http.ResponseWriter, status int, value any) error { writer.Header().Add("Content-Type", "application/json") writer.WriteHeader(status) @@ -46,15 +46,15 @@ func validateLimit(strLimit string) int { // strLimit := req.URL.Query().Get("limit") log.Printf("strLimit is: %s", strLimit) limit := -1 - if strLimit != "" { + if strLimit != "" { var err error - limit, err = strconv.Atoi(strLimit) - if err != nil || limit < -1 { + limit, err = strconv.Atoi(strLimit) + if err != nil || limit < -1 { fmt.Printf("limit query param invalid: %s. Aborting..", err) return -1 - } + } - } + } log.Printf("limit is: %d", limit) return limit } @@ -62,14 +62,14 @@ func validateLimit(strLimit string) int { func validateOffset(strOffset string) int { log.Printf("strOffset is: %s", strOffset) offset := -1 - if strOffset != "" { + if strOffset != "" { var err error - offset, err = strconv.Atoi(strOffset) - if err != nil || offset < -1 { + offset, err = strconv.Atoi(strOffset) + if err != nil || offset < -1 { fmt.Printf("offset query param invalid: %s. Aborting..", err) return -1 - } - } + } + } log.Printf("offset is: %d", offset) return offset } @@ -77,8 +77,8 @@ func validateOffset(strOffset string) int { func validateColName(strOrderColName string) (string, error) { log.Printf("strOrderColName is: %s", strOrderColName) orderColName := "serviceId" - if strOrderColName != "" { - var existingColumnNames = []string{"serviceid","servicename","servicedescription","serviceversions","createdat"} + if strOrderColName != "" { + existingColumnNames := []string{"serviceid", "servicename", "servicedescription", "serviceversions", "createdat"} if slices.Contains(existingColumnNames, strOrderColName) { orderColName = strOrderColName } else { @@ -87,15 +87,15 @@ func validateColName(strOrderColName string) (string, error) { fmt.Println(colNameErr) return "", colNameErr } - } + } return orderColName, nil } func validateOrderDir(strOrderDir string) (string, error) { log.Printf("strOrderDir is: %s", strOrderDir) orderDir := "asc" - if strOrderDir != "" { - existingDirectionNames := []string{"asc","desc"} + if strOrderDir != "" { + existingDirectionNames := []string{"asc", "desc"} if slices.Contains(existingDirectionNames, strOrderDir) { orderDir = strOrderDir } else { @@ -104,14 +104,13 @@ func validateOrderDir(strOrderDir string) (string, error) { fmt.Println(directionNamesErr) return "", directionNamesErr } - } + } log.Printf("orderDir is: %s", orderDir) return orderDir, nil } - -//////////////////////////////////////////////////////// -//////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////// func makeHTTPHandleFunc(f ApiFunc) http.HandlerFunc { return func(writer http.ResponseWriter, req *http.Request) { if err := f(writer, req); err != nil { @@ -121,16 +120,16 @@ func makeHTTPHandleFunc(f ApiFunc) http.HandlerFunc { } } -// @Summary Get a list of all services -// @Id 1 -// @produce application/json -// @Failure 500 message "Server Error: $err" -// @Failure 400 message "limit query param invalid: $err" -// @Failure 400 message "offset query param invalid: $err" -// @Success 200 message model.Service -// @Router /services [get, head] +// @Summary Get a list of all services +// @Id 1 +// @produce application/json +// @Failure 500 message "Server Error: $err" +// @Failure 400 message "limit query param invalid: $err" +// @Failure 400 message "offset query param invalid: $err" +// @Success 200 message model.Service +// @Router /services [get, head] func (server *APIServer) handleGetAllServices(writer http.ResponseWriter, req *http.Request) error { - //https://stackoverflow.com/questions/57776448/stop-processing-of-http-request-in-go + // https://stackoverflow.com/questions/57776448/stop-processing-of-http-request-in-go strLimit := req.URL.Query().Get("limit") limit := validateLimit(strLimit) @@ -140,7 +139,7 @@ func (server *APIServer) handleGetAllServices(writer http.ResponseWriter, req *h return WriteJson(writer, http.StatusBadRequest, fmt.Sprintf("Error 400: limit query param %s is too high. Max allowed is 20", strLimit)) } - strOffset := req.URL.Query().Get("offset") + strOffset := req.URL.Query().Get("offset") offset := validateOffset(strOffset) if offset == -1 { return WriteJson(writer, http.StatusBadRequest, fmt.Sprintf("Error 400: offset query param invalid: %s. Must be an int.", strOffset)) @@ -168,25 +167,24 @@ func (server *APIServer) handleGetAllServices(writer http.ResponseWriter, req *h return WriteJson(writer, http.StatusOK, serviceSlice) } -// @Summary Create new service -// @Id 2 -// @produce application/json -// @Param mode.CreateServiceRequest body message true -// @Failure 500 message "Server Error: $err" -// @Failure 415 message "Content-Type header is not application/json" -// @Failure 413 message "Request body must not be larger than 1MB" -// @Failure 404 message "404 page not found" -// @Failure 400 message "Request body contains badly-formed JSON (at position x)" -// @Failure 400 message "Request body contains badly-formed JSON" -// @Failure 400 message "Request body contains an invalid value for the y field (at position x)" -// @Failure 400 message "Request body contains unknown field y" -// @Failure 400 message "Request body must not be empty" -// @Failure 400 message "Error default 500: err" -// @Failure 400 message "Request body must only contain a single JSON object" -// @Success 201 message model.Service -// @Router /services/new [post, head] +// @Summary Create new service +// @Id 2 +// @produce application/json +// @Param mode.CreateServiceRequest body message true +// @Failure 500 message "Server Error: $err" +// @Failure 415 message "Content-Type header is not application/json" +// @Failure 413 message "Request body must not be larger than 1MB" +// @Failure 404 message "404 page not found" +// @Failure 400 message "Request body contains badly-formed JSON (at position x)" +// @Failure 400 message "Request body contains badly-formed JSON" +// @Failure 400 message "Request body contains an invalid value for the y field (at position x)" +// @Failure 400 message "Request body contains unknown field y" +// @Failure 400 message "Request body must not be empty" +// @Failure 400 message "Error default 500: err" +// @Failure 400 message "Request body must only contain a single JSON object" +// @Success 201 message model.Service +// @Router /services/new [post, head] func (server *APIServer) handleCreateNewService(writer http.ResponseWriter, req *http.Request) error { - log.Println("Creating new service") createServReq := new(models.CreateServiceRequest) log.Printf("createServReq: %s", createServReq) @@ -217,15 +215,15 @@ func (server *APIServer) handleCreateNewService(writer http.ResponseWriter, req return WriteJson(writer, http.StatusCreated, service) } -// @Summary Get service by Id -// @Id 3 -// @produce application/json -// @Param serviceId query int true "serviceId" -// @Failure 500 message "Server Error: $err" -// @Failure 400 message "Error 400: $err" -// @Failure 404 message "404 page not found" -// @Success 200 message model.Service -// @Router /services/id/{ServiceId:[0-9]+} [get, head] +// @Summary Get service by Id +// @Id 3 +// @produce application/json +// @Param serviceId query int true "serviceId" +// @Failure 500 message "Server Error: $err" +// @Failure 400 message "Error 400: $err" +// @Failure 404 message "404 page not found" +// @Success 200 message model.Service +// @Router /services/id/{ServiceId:[0-9]+} [get, head] func (server *APIServer) handleGetServiceById(writer http.ResponseWriter, req *http.Request) error { serviceId, err := getServiceId(req) if err != nil { @@ -234,7 +232,6 @@ func (server *APIServer) handleGetServiceById(writer http.ResponseWriter, req *h return WriteJson(writer, http.StatusBadRequest, err) } - fmt.Printf("checking for serviceId %d", serviceId) service, err := server.db.GetServiceById(serviceId) if err != nil { @@ -246,16 +243,16 @@ func (server *APIServer) handleGetServiceById(writer http.ResponseWriter, req *h return WriteJson(writer, http.StatusOK, service) } -// @Summary Delete service by Id -// @Id 4 -// @produce application/json -// @Param serviceId query int true "serviceId" -// @Failure 500 message "Server Error: $err" -// @Failure 400 message "Error 400: $err" -// @Failure 404 message "404 page not found" -// @Failure 404 message "Could not find serviceId: $serviceId" -// @Success 200 message "deleted $serviceId" -// @Router /services/id/{ServiceId:[0-9]+} [delete, head] +// @Summary Delete service by Id +// @Id 4 +// @produce application/json +// @Param serviceId query int true "serviceId" +// @Failure 500 message "Server Error: $err" +// @Failure 400 message "Error 400: $err" +// @Failure 404 message "404 page not found" +// @Failure 404 message "Could not find serviceId: $serviceId" +// @Success 200 message "deleted $serviceId" +// @Router /services/id/{ServiceId:[0-9]+} [delete, head] func (server *APIServer) handleDeleteServiceById(writer http.ResponseWriter, req *http.Request) error { serviceId, err := getServiceId(req) if err != nil { @@ -281,15 +278,15 @@ func (server *APIServer) handleDeleteServiceById(writer http.ResponseWriter, req return WriteJson(writer, http.StatusOK, map[string]int{"deleted": serviceId}) } -// @Summary Get service versions by Id -// @Id 5 -// @produce application/json -// @Param serviceId query int true "serviceId" -// @Failure 400 message "Error 400: $err" -// @Failure 404 message "404 page not found" -// @Failure 500 message "Server Error: $err" -// @Success 200 message serviceVersions -// @Router /services/id/{ServiceId:[0-9]+} [get, head] +// @Summary Get service versions by Id +// @Id 5 +// @produce application/json +// @Param serviceId query int true "serviceId" +// @Failure 400 message "Error 400: $err" +// @Failure 404 message "404 page not found" +// @Failure 500 message "Server Error: $err" +// @Success 200 message serviceVersions +// @Router /services/id/{ServiceId:[0-9]+} [get, head] func (server *APIServer) handleGetServiceVersionsById(writer http.ResponseWriter, req *http.Request) error { serviceId, err := getServiceId(req) if err != nil { @@ -311,13 +308,13 @@ func (server *APIServer) handleGetServiceVersionsById(writer http.ResponseWriter return WriteJson(writer, http.StatusOK, serviceVersions) } -// @Summary Get service by name -// @Id 6 -// @produce application/json -// @Param serviceName query string true "serviceName" -// @Failure 500 message "Server Error: $err" -// @Success 200 message service -// @Router /services/name/{ServiceName:[a-zA-Z0-9]+} [get, head] +// @Summary Get service by name +// @Id 6 +// @produce application/json +// @Param serviceName query string true "serviceName" +// @Failure 500 message "Server Error: $err" +// @Success 200 message service +// @Router /services/name/{ServiceName:[a-zA-Z0-9]+} [get, head] func (server *APIServer) handleGetServiceByName(writer http.ResponseWriter, req *http.Request) error { // TODO: validate mux var (400 error) serviceName := mux.Vars(req)["ServiceName"] @@ -334,11 +331,11 @@ func (server *APIServer) handleGetServiceByName(writer http.ResponseWriter, req return WriteJson(writer, http.StatusOK, service) } -// @Summary Get API health -// @Id 6 -// @produce application/json -// @Success 200 message "service is up and running" -// @Router /health [get, head] +// @Summary Get API health +// @Id 6 +// @produce application/json +// @Success 200 message "service is up and running" +// @Router /health [get, head] func (server *APIServer) handleGetHealth(writer http.ResponseWriter, req *http.Request) error { return WriteJson(writer, http.StatusOK, "service is up and running") } diff --git a/pkg/api/apiNewServer.go b/pkg/api/apiNewServer.go index 6c5b6c72..100b9330 100644 --- a/pkg/api/apiNewServer.go +++ b/pkg/api/apiNewServer.go @@ -36,13 +36,12 @@ func (server *APIServer) Run(port string) { // docs router.PathPrefix("/swagger/").Handler(httpSwagger.Handler( // httpSwagger.URL(fmt.Sprintf("http://localhost:%s/swagger/swagger.json", port)), // buggy - httpSwagger.URL("http://localhost:8969/swagger/swagger.json"), //The url pointing to API definition + httpSwagger.URL("http://localhost:8969/swagger/swagger.json"), // The url pointing to API definition httpSwagger.DeepLinking(true), httpSwagger.DocExpansion("none"), httpSwagger.DomID("swagger-ui"), )).Methods(http.MethodGet) - log.Println("API server running on port: ", server.listenAddr) log.Fatal(http.ListenAndServe(server.listenAddr, router)) -} \ No newline at end of file +} diff --git a/pkg/config/config.go b/pkg/config/config.go index f8ff01db..9d885261 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -12,13 +12,13 @@ import ( // DatabaseConfigurations exported type Configurations struct { - APIPort string `mapstructure:"API_PORT" validate:"required"` - DBName string `mapstructure:"DB_NAME" validate:"required"` - DBUser string `mapstructure:"DB_USER" validate:"required"` - DBPassword string `mapstructure:"DB_PASSWORD" validate:"required"` - DBSSLmode string `mapstructure:"DB_SSLMODE" validate:"required"` - DBMaxOpenConns int `mapstructure:"DB_MAX_OPEN_CONNS" validate:"required"` - DBMaxIdleConns int `mapstructure:"DB_MAX_IDLE_CONNS"` + APIPort string `mapstructure:"API_PORT" validate:"required"` + DBName string `mapstructure:"DB_NAME" validate:"required"` + DBUser string `mapstructure:"DB_USER" validate:"required"` + DBPassword string `mapstructure:"DB_PASSWORD" validate:"required"` + DBSSLmode string `mapstructure:"DB_SSLMODE" validate:"required"` + DBMaxOpenConns int `mapstructure:"DB_MAX_OPEN_CONNS" validate:"required"` + DBMaxIdleConns int `mapstructure:"DB_MAX_IDLE_CONNS"` } // function to get characters from strings using index @@ -43,11 +43,10 @@ func printMaskedSecret(secret string, shownChars int) string { } } - return strings.Join(strSlice,"") + return strings.Join(strSlice, "") } func Readconfig(configFileNameRoot, configFileNameExt string) (configurations *Configurations, err error) { - // Set the path to look for the configurations file viper.AddConfigPath(".") @@ -59,23 +58,23 @@ func Readconfig(configFileNameRoot, configFileNameExt string) (configurations *C viper.AutomaticEnv() err = viper.ReadInConfig() - if err != nil { - log.Fatalf("unable to read configfile %v", err) + if err != nil { + log.Fatalf("unable to read configfile %v", err) os.Exit(42) - } + } - if uerr := viper.UnmarshalExact(&configurations); uerr!=nil { - log.Fatalf("unable to unmarshall configfile %v", uerr) + if uerr := viper.UnmarshalExact(&configurations); uerr != nil { + log.Fatalf("unable to unmarshall configfile %v", uerr) os.Exit(42) - } - validate := validator.New(validator.WithRequiredStructEnabled()) - if verr := validate.Struct(configurations); verr!=nil{ - log.Fatalf("Missing required attributes %v\n", verr) + } + validate := validator.New(validator.WithRequiredStructEnabled()) + if verr := validate.Struct(configurations); verr != nil { + log.Fatalf("Missing required attributes %v\n", verr) for _, xerr := range verr.(validator.ValidationErrors) { fmt.Println(xerr.Field(), xerr.Tag()) } os.Exit(42) - } + } // Reading variables using the config struct fmt.Println("Reading variables using the config struct..") @@ -87,10 +86,8 @@ func Readconfig(configFileNameRoot, configFileNameExt string) (configurations *C fmt.Println("DBMaxOpenConns is\t\t", configurations.DBMaxOpenConns) fmt.Println("DBMaxIdleConns is\t\t", configurations.DBMaxIdleConns) - - return } // https://medium.com/@bnprashanth256/reading-configuration-files-and-environment-variables-in-go-golang-c2607f912b63 -// https://dev.to/techschoolguru/load-config-from-file-environment-variables-in-golang-with-viper-2j2d \ No newline at end of file +// https://dev.to/techschoolguru/load-config-from-file-environment-variables-in-golang-with-viper-2j2d diff --git a/pkg/models/dbConnector.go b/pkg/models/dbConnector.go index e9de3c4d..9b78ae15 100644 --- a/pkg/models/dbConnector.go +++ b/pkg/models/dbConnector.go @@ -13,7 +13,7 @@ type Dbase interface { GetServiceById(int) (*Service, error) DeleteServiceById(int) (int64, error) GetServiceVersionsById(int) (string, error) - CreateNewService(*Service) (*Service, error) + CreateNewService(*Service) (*Service, error) } type PostgresDb struct { @@ -26,7 +26,6 @@ func NewPostgresDb(userName, dbName, password, sslmode string, maxOpenConns, max connStr := fmt.Sprintf("user=%s dbname=%s password=%s sslmode=%s", userName, dbName, password, sslmode) log.Printf("connStr: %s", connStr) - db, err := sql.Open("postgres", connStr) if err != nil { log.Fatal(err) @@ -37,7 +36,7 @@ func NewPostgresDb(userName, dbName, password, sslmode string, maxOpenConns, max // https://www.alexedwards.net/blog/configuring-sqldb db.SetMaxOpenConns(maxOpenConns) // Sane default db.SetMaxIdleConns(maxIdleConns) // try 2 for some performance gains - db.SetConnMaxLifetime(5*time.Minute) + db.SetConnMaxLifetime(5 * time.Minute) if err := db.Ping(); err != nil { log.Printf("Error: %s", err) @@ -48,9 +47,8 @@ func NewPostgresDb(userName, dbName, password, sslmode string, maxOpenConns, max return &PostgresDb{ db: db, }, nil - } func (db *PostgresDb) Init() error { return db.CreateTable() -} \ No newline at end of file +} diff --git a/pkg/models/dbHelpers.go b/pkg/models/dbHelpers.go index 52783912..3487025c 100644 --- a/pkg/models/dbHelpers.go +++ b/pkg/models/dbHelpers.go @@ -22,10 +22,9 @@ func (db *PostgresDb) CreateTable() error { return err } -//////////////////////////////////////////////////////// -//////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////// func (db *PostgresDb) GetAllServices(orderColName, orderDirection string, limit, offset int) ([]*Service, error) { - log.Println("Looking up services in DB") // check to see if this is good against sql injection @@ -49,7 +48,7 @@ func (db *PostgresDb) GetAllServices(orderColName, orderDirection string, limit, println() for rows.Next() { service, err := scanService(rows) - if err !=nil { + if err != nil { log.Printf("Error: %s", err) return nil, err } @@ -185,8 +184,8 @@ func (db *PostgresDb) GetServiceVersionsById(ServiceId int) (string, error) { return serviceVersions, nil } -//////////////////////////////////////////////////////// -//////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////// func scanService(rows *sql.Rows) (*Service, error) { service := new(Service) err := rows.Scan( diff --git a/pkg/models/dbTypes.go b/pkg/models/dbTypes.go index 4bf03dea..9393037f 100644 --- a/pkg/models/dbTypes.go +++ b/pkg/models/dbTypes.go @@ -23,27 +23,25 @@ func makeRandomVersionsSlice(max int) string { // TODO: validate for empty strings type CreateServiceRequest struct { - ServiceName string `json:"serviceName" validate:"required"` + ServiceName string `json:"serviceName" validate:"required"` ServiceDescription string `json:"serviceDescription" validate:"required"` - ServiceVersions string `json:"serviceVersions"` + ServiceVersions string `json:"serviceVersions"` } // Response type Service struct { - ServiceId int `json:"serviceId"` - ServiceName string `json:"serviceName"` - ServiceDescription string `json:"serviceDescription"` - ServiceVersions string `json:"serviceVersions"` - CreatedAt time.Time `json:"createdAt"` + ServiceId int `json:"serviceId"` + ServiceName string `json:"serviceName"` + ServiceDescription string `json:"serviceDescription"` + ServiceVersions string `json:"serviceVersions"` + CreatedAt time.Time `json:"createdAt"` } - func NewService(ServiceName, ServiceDescription string) *Service { return &Service{ - ServiceName: ServiceName, + ServiceName: ServiceName, ServiceDescription: ServiceDescription, - ServiceVersions: makeRandomVersionsSlice(5), - CreatedAt: time.Now().UTC(), + ServiceVersions: makeRandomVersionsSlice(5), + CreatedAt: time.Now().UTC(), } } -