diff --git a/api/README.md b/api/README.md index fdb44d6514b31fed63d12d8e90f73fc007b8e20e..a464c77100fefa86aa1fbef21ba3cdbeee40b810 100644 --- a/api/README.md +++ b/api/README.md @@ -58,25 +58,6 @@ The main function of your API must: "del-user": users.DelHandler, } -4) Define a Local Port (for testing only) - - You could define a local TCP port using a flag, which will allow you to run from the console using command line without using mage. - - This feature is working but still under development, so rather not use it and run in mage where you have the correct environment setup defined in CDK. - - When used, it runs with a normal golang HTTP server, not AWS Lambda. It also supports concurrent processing of multiple HTTP requests in one image, which AWS Lambda never does. - - Example: - - localPortPtr := flag.Int("port", 0, "Local HTTP Port") - flag.Parse() - - .WithLocalPort(localPortPtr) - - When the port is nil or has value 0, it will not apply and run as AWS Lambda. - - So when mage runs your executable without the -port=xxx option, the local port will not be used and your API runs as an AWS Lambda function. - 6) Run() Now the service definition is complete, it must run. diff --git a/api/api.go b/api/api.go index 2036cbe31af0da75c4df5a3cb223ded63fcc58a7..2b4e1041cd2263fcaba511384c252aa697994a27 100644 --- a/api/api.go +++ b/api/api.go @@ -2,7 +2,6 @@ package api import ( "fmt" - "net/http" "os" "runtime/debug" @@ -39,7 +38,6 @@ func New(requestIDHeaderKey string, routes map[string]map[string]interface{}) Ap checks: map[string]ICheck{}, crashReporter: defaultCrashReporter{}, cors: nil, - localPort: 0, localQueueEventHandlers: nil, } } @@ -51,7 +49,6 @@ type Api struct { checks map[string]ICheck crashReporter ICrashReporter cors ICORS - localPort int //==0 for default lambda, >0 for http.ListenAndServe to run locally localQueueEventHandlers map[string]interface{} //only applies when running locally for local in-memory queues } @@ -61,12 +58,6 @@ func (api Api) WithStarter(name string, starter service.IStarter) Api { return api } -//wrap Service.WithErrorReporter to return api, else cannot be chained -func (api Api) WithErrorReporter(reporter service.IErrorReporter) Api { - api.Service = api.Service.WithErrorReporter(reporter) - return api -} - //wrap else cannot be chained func (api Api) WithAuditor(auditor audit.Auditor) Api { api.Service = api.Service.WithAuditor(auditor) @@ -119,18 +110,6 @@ func (api Api) WithCrashReported(crashReporter ICrashReporter) Api { return api } -//If local port is defined (!=nil and >0) then the lambda function -//is replaced with a local HTTP server -func (api Api) WithLocalPort(localPortPtr *int) Api { - if api.localPort != 0 { - panic("local port already defined") - } - if localPortPtr != nil && *localPortPtr > 0 { - api.localPort = *localPortPtr - } - return api -} - //WithEvents are not used in production, only when env LOG_LEVEL=debug //then the SQS producer is replaced with in-memory producer that uses //go channels to queue and process events, so they can be debugged locally @@ -145,28 +124,18 @@ func (api Api) WithEvents(eventHandlers map[string]interface{}) Api { //run and panic on error func (api Api) Run() { //decide local or SQS - if (api.localPort > 0 || os.Getenv("LOG_LEVEL") == "debug") && api.localQueueEventHandlers != nil { + if (os.Getenv("LOG_LEVEL") == "debug") && api.localQueueEventHandlers != nil { //use in-memory channels for async events api.Debugf("Using in-memory channels for async events ...") - memConsumer := queues_mem.NewConsumer(api.Service, api.localQueueEventHandlers) - api = api.WithProducer(queues_mem.NewProducer(memConsumer)) + api = api.WithProducer(queues_mem.NewProducer(queues_mem.NewConsumer(api.Service, api.localQueueEventHandlers))) } else { //use SQS for async events api.Debugf("Using SQS queue producer for async events ...") api = api.WithProducer(queues_sqs.NewProducer(api.requestIDHeaderKey)) } - //decide local of lambda - if api.localPort > 0 { - //running locally with standard HTTP server - err := http.ListenAndServe(fmt.Sprintf(":%d", api.localPort), api) //calls api.ServeHTTP() which calls api.Handler() - if err != nil { - panic(err) - } - } else { - //run as an AWS Lambda function - lambda.Start(api.Handler) - } + //run as an AWS Lambda function + lambda.Start(api.Handler) } type defaultCrashReporter struct{} diff --git a/api/lambda.go b/api/lambda.go index c36be3b0a7dfff84fc761509603e54dd684c3154..d18f1316f2e114ec6a1cc40a57a62cfd6207290e 100644 --- a/api/lambda.go +++ b/api/lambda.go @@ -93,7 +93,6 @@ func (api Api) Handler(baseCtx context.Context, apiGatewayProxyReq events.APIGat jsonError, _ := json.Marshal(map[string]interface{}{"message": errorMessage}) res.Headers["Content-Type"] = "application/json" res.Body = string(jsonError) - api.Service.ReportError(ctx.Data(), err) err = nil //never pass error back to lambda or http server } if api.requestIDHeaderKey != "" { diff --git a/api/local.go b/api/local.go deleted file mode 100644 index 0aa903a5b342883e7fe57fcf2ccfd2c22370df13..0000000000000000000000000000000000000000 --- a/api/local.go +++ /dev/null @@ -1,102 +0,0 @@ -package api - -import ( - "bytes" - "context" - "net/http" - "time" - - "github.com/aws/aws-lambda-go/events" - "github.com/google/uuid" - "gitlab.com/uafrica/go-utils/errors" -) - -//use this in http.ListenAndServe() to test locally -func (api Api) ServeHTTP(httpRes http.ResponseWriter, httpReq *http.Request) { - api.Debugf("HTTP %s %s", httpReq.Method, httpReq.URL.Path) - ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) - defer cancel() - - req := events.APIGatewayProxyRequest{ - Resource: httpReq.URL.Path, //RequestURI cannot be used - it includes the url params - Path: httpReq.URL.Path, - HTTPMethod: httpReq.Method, - Headers: map[string]string{}, - MultiValueHeaders: nil, // map[string][]string `json:"multiValueHeaders"` - QueryStringParameters: map[string]string{}, - MultiValueQueryStringParameters: map[string][]string{}, - PathParameters: nil, // map[string]string `json:"pathParameters"` - StageVariables: nil, // map[string]string `json:"stageVariables"` - IsBase64Encoded: false, - } - - //copy significant headers - //todo: option to let app specify other list - for _, name := range []string{"Content-Type", "request-id"} { - if value := httpReq.Header.Get(name); value != "" { - req.Headers[name] = value - } - } - - requestID := httpReq.Header.Get("request-id") - if requestID == "" { - //define a random request-id - requestID = uuid.New().String() - req.Headers["request-id"] = requestID - } - - for n, v := range httpReq.URL.Query() { - if len(v) == 1 { - req.QueryStringParameters[n] = v[0] - } - if len(v) > 1 { - req.MultiValueQueryStringParameters[n] = v - } - } - - req.RequestContext = events.APIGatewayProxyRequestContext{ - AccountID: "", //string `json:"accountId"` - ResourceID: "", //string `json:"resourceId"` - OperationName: "", //string `json:"operationName,omitempty"` - Stage: "", //string `json:"stage"` - DomainName: "", //string `json:"domainName"` - DomainPrefix: "", //string `json:"domainPrefix"` - RequestID: requestID, //string `json:"requestId"` - Protocol: "", //string `json:"protocol"` - Identity: events.APIGatewayRequestIdentity{}, // `json:"identity"` - ResourcePath: "", //string `json:"resourcePath"` - Authorizer: nil, //map[string]interface{} `json:"authorizer"` - HTTPMethod: "", //string `json:"httpMethod"` - RequestTime: "", //string `json:"requestTime"` - RequestTimeEpoch: time.Now().Unix(), // `json:"requestTimeEpoch"` - APIID: "", //string `json:"apiId"` // The API Gateway rest API Id - } - - bodyBytes := bytes.NewBuffer(nil) - if _, err := bodyBytes.ReadFrom(httpReq.Body); err != nil { - http.Error(httpRes, errors.Wrapf(err, "failed to read body").Error(), http.StatusInternalServerError) - return - } - req.Body = bodyBytes.String() - - res, err := api.Handler(ctx, req) - if err != nil { - http.Error(httpRes, err.Error(), http.StatusInternalServerError) - return - } - - for n, v := range res.Headers { - httpRes.Header().Set(n, v) - } - if res.StatusCode < 200 || res.StatusCode >= 300 { - httpRes.Header().Set("X-Content-Type-Options", "nosniff") - httpRes.Header().Set("Content-Type", "application/json") - httpRes.WriteHeader(res.StatusCode) - httpRes.Write([]byte(res.Body)) //assuming this is a JSON error message - return - } - - if res.Body != "" { - httpRes.Write([]byte(res.Body)) - } -} diff --git a/cron/cron.go b/cron/cron.go index 316d547645a950921bc465dd1d3d967677926890..5a9dff266b6ef2411f276cefa5599b1199b839ed 100644 --- a/cron/cron.go +++ b/cron/cron.go @@ -49,12 +49,6 @@ func (cron Cron) WithStarter(name string, starter service.IStarter) Cron { return cron } -//wrap Service.WithErrorReporter to return api, else cannot be chained -func (cron Cron) WithErrorReporter(reporter service.IErrorReporter) Cron { - cron.Service = cron.Service.WithErrorReporter(reporter) - return cron -} - //wrap else cannot be chained func (cron Cron) WithAuditor(auditor audit.Auditor) Cron { cron.Service = cron.Service.WithAuditor(auditor) @@ -124,7 +118,6 @@ func (cron Cron) Run(invokeArn *string) { err := cron.Handler(lambdaContext) if err != nil { logger.Errorf("local cron handler failed: %+v", err) - //cron.Service.ReportError(nil, err) } else { logger.Debugf("local cron success") } diff --git a/cron/lambda.go b/cron/lambda.go index e2843bf39996902799a95bb6a17e61c5aed500ce..3043aaacb63bac05c1d6625fdbc4f627d2212721 100644 --- a/cron/lambda.go +++ b/cron/lambda.go @@ -66,7 +66,6 @@ func (cron Cron) Handler(lambdaCtx context.Context) (err error) { ctx.Infof("Start CRON Handler") if err := cronFunc(ctx); err != nil { - cron.Service.ReportError(ctx.Data(), err) return errors.Wrapf(err, "Cron(%s) failed", cronName) } return nil diff --git a/queues/consumer.go b/queues/consumer.go index a476ddc1a994ea154851c499b652c753fb148e5e..ec4c50db8dc3f9b01f514ed232dac34285337e86 100644 --- a/queues/consumer.go +++ b/queues/consumer.go @@ -5,7 +5,6 @@ import "gitlab.com/uafrica/go-utils/service" //IConsumer is the interface implemented by both mem and sqs consumer type Consumer interface { WithStarter(name string, starter service.IStarter) Consumer - WithErrorReporter(reporter service.IErrorReporter) Consumer Run() ProcessFile(filename string) error } diff --git a/queues/mem/consumer.go b/queues/mem/consumer.go index e4b1756e537854e476441d5c408a39f8006aec86..f2c33116343fc1f61fca466b4d4e5d7c8234ef1b 100644 --- a/queues/mem/consumer.go +++ b/queues/mem/consumer.go @@ -52,12 +52,6 @@ func (consumer *consumer) WithStarter(name string, starter service.IStarter) que return consumer } -//wrap Service.WithErrorReporter to return api, else cannot be chained -func (consumer *consumer) WithErrorReporter(reporter service.IErrorReporter) queues.Consumer { - consumer.Service = consumer.Service.WithErrorReporter(reporter) - return consumer -} - //wrap else cannot be chained func (consumer *consumer) WithAuditor(auditor audit.Auditor) queues.Consumer { consumer.Service = consumer.Service.WithAuditor(auditor) diff --git a/queues/sqs/consumer.go b/queues/sqs/consumer.go index cddbef85ef6f43920464b12643035435fa74af23..1c31e5f6412fb8f29b15f016a645e9c12874326c 100644 --- a/queues/sqs/consumer.go +++ b/queues/sqs/consumer.go @@ -67,12 +67,6 @@ func (consumer consumer) WithStarter(name string, starter service.IStarter) queu return consumer } -//wrap Service.WithErrorReporter to return api, else cannot be chained -func (consumer consumer) WithErrorReporter(reporter service.IErrorReporter) queues.Consumer { - consumer.Service = consumer.Service.WithErrorReporter(reporter) - return consumer -} - //wrap else cannot be chained func (consumer consumer) WithAuditor(auditor audit.Auditor) queues.Consumer { consumer.Service = consumer.Service.WithAuditor(auditor) @@ -202,7 +196,6 @@ func (consumer consumer) Handler(baseCtx context.Context, lambdaEvent events.SQS results := handler.FuncValue.Call(args) if len(results) > 0 && !results[0].IsNil() { ctx.Errorf("handler failed: %+v", results[0].Interface().(error)) - consumer.Service.ReportError(ctx.Data(), err) } } } diff --git a/service/service.go b/service/service.go index 9d6d83ee6c4a74a660fe990b43fb5a325525a63b..0a80933a8f94edc92eaf69d8817a390c1b474f2c 100644 --- a/service/service.go +++ b/service/service.go @@ -12,13 +12,11 @@ import ( type Service interface { logger.Logger - IErrorReporter Producer audit.Auditor WithStarter(name string, starter IStarter) Service WithProducer(producer Producer) Service WithAuditor(auditor audit.Auditor) Service - WithErrorReporter(reporter IErrorReporter) Service NewContext(base context.Context, requestID string, values map[string]interface{}) (Context, error) } @@ -28,19 +26,17 @@ func New() Service { env = "dev" } return service{ - Producer: nil, - Logger: logger.New().WithFields(map[string]interface{}{"env": env}), - IErrorReporter: DoNotReportErrors{}, - Auditor: audit.None(), - env: env, - starters: map[string]IStarter{}, + Producer: nil, + Logger: logger.New().WithFields(map[string]interface{}{"env": env}), + Auditor: audit.None(), + env: env, + starters: map[string]IStarter{}, } } type service struct { logger.Logger //for logging outside of context Producer //for sending async events - IErrorReporter audit.Auditor env string starters map[string]IStarter @@ -84,25 +80,9 @@ func (s service) WithProducer(producer Producer) Service { return s } -func (s service) WithErrorReporter(reporter IErrorReporter) Service { - if reporter == nil { - panic(errors.Errorf("ErrorReporter==nil")) - } - s.IErrorReporter = reporter - return s -} - func (s service) WithAuditor(auditor audit.Auditor) Service { if auditor != nil { s.Auditor = auditor } return s } - -type IErrorReporter interface { - ReportError(fields map[string]interface{}, err error) -} - -type DoNotReportErrors struct{} - -func (DoNotReportErrors) ReportError(fields map[string]interface{}, err error) {}