Skip to content

Commit

Permalink
Merge pull request #1431 from lenny-intel/app-ctx
Browse files Browse the repository at this point in the history
feat: Add API to get SDK's App Context
  • Loading branch information
Lenny Goodell authored Jun 28, 2023
2 parents 34e8d8f + af96062 commit 5ed1430
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 0 deletions.
6 changes: 6 additions & 0 deletions app-service-template/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package main

import (
"context"
"os"
"reflect"

Expand All @@ -38,6 +39,7 @@ const (
type myApp struct {
service interfaces.ApplicationService
lc logger.LoggingClient
appCtx context.Context
serviceConfig *config.ServiceConfig
configChanged chan bool
}
Expand Down Expand Up @@ -140,6 +142,10 @@ func (app *myApp) CreateAndRunAppService(serviceKey string, newServiceFactory fu
return -1
}

// TODO: Use this context in long running function to detect when the context is cancel for function can exit.
// Remove if no long running functions
app.appCtx = app.service.AppContext()

if err := app.service.Run(); err != nil {
app.lc.Errorf("Run returned error: %s", err.Error())
return -1
Expand Down
5 changes: 5 additions & 0 deletions app-service-template/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package main

import (
"context"
"fmt"
"testing"

Expand All @@ -39,6 +40,7 @@ func TestCreateAndRunService_Success(t *testing.T) {

mockFactory := func(_ string) (interfaces.ApplicationService, bool) {
mockAppService := &mocks.ApplicationService{}
mockAppService.On("AppContext").Return(context.Background())
mockAppService.On("LoggingClient").Return(logger.NewMockClient())
mockAppService.On("GetAppSettingStrings", "DeviceNames").
Return([]string{"Random-Boolean-Device, Random-Integer-Device"}, nil)
Expand Down Expand Up @@ -81,6 +83,7 @@ func TestCreateAndRunService_GetAppSettingStrings_Failed(t *testing.T) {
getAppSettingStringsCalled := false
mockFactory := func(_ string) (interfaces.ApplicationService, bool) {
mockAppService := &mocks.ApplicationService{}
mockAppService.On("AppContext").Return(context.Background())
mockAppService.On("LoggingClient").Return(logger.NewMockClient())
mockAppService.On("GetAppSettingStrings", "DeviceNames").
Return(nil, fmt.Errorf("Failed")).Run(func(args mock.Arguments) {
Expand All @@ -104,6 +107,7 @@ func TestCreateAndRunService_SetFunctionsPipeline_Failed(t *testing.T) {

mockFactory := func(_ string) (interfaces.ApplicationService, bool) {
mockAppService := &mocks.ApplicationService{}
mockAppService.On("AppContext").Return(context.Background())
mockAppService.On("LoggingClient").Return(logger.NewMockClient())
mockAppService.On("GetAppSettingStrings", "DeviceNames").
Return([]string{"Random-Boolean-Device, Random-Integer-Device"}, nil)
Expand Down Expand Up @@ -137,6 +141,7 @@ func TestCreateAndRunService_Run_Failed(t *testing.T) {

mockFactory := func(_ string) (interfaces.ApplicationService, bool) {
mockAppService := &mocks.ApplicationService{}
mockAppService.On("AppContext").Return(context.Background())
mockAppService.On("LoggingClient").Return(logger.NewMockClient())
mockAppService.On("GetAppSettingStrings", "DeviceNames").
Return([]string{"Random-Boolean-Device, Random-Integer-Device"}, nil)
Expand Down
6 changes: 6 additions & 0 deletions internal/app/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,12 @@ type contextGroup struct {
stop context.CancelFunc
}

// AppContext returns the application service context used to detect cancelled context when the service is terminating.
// Used by custom app service to appropriately exit any long-running functions.
func (svc *Service) AppContext() context.Context {
return svc.ctx.appCtx
}

// AddRoute allows you to leverage the existing webserver to add routes.
func (svc *Service) AddRoute(route string, handler func(nethttp.ResponseWriter, *nethttp.Request), methods ...string) error {
if route == commonConstants.ApiPingRoute ||
Expand Down
15 changes: 15 additions & 0 deletions internal/app/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package app

import (
"context"
"fmt"
"net/http"
"os"
Expand Down Expand Up @@ -964,3 +965,17 @@ func TestService_SecretProvider(t *testing.T) {
require.NotNil(t, actual)
assert.Equal(t, mockSecretProvider, actual)
}

func TestService_AppContext(t *testing.T) {
expected, cancel := context.WithCancel(context.Background())
sdk := Service{
ctx: contextGroup{
appCtx: expected,
},
}

actual := sdk.AppContext()
assert.Equal(t, expected, actual)
// Linter requires use cancel function
cancel()
}
18 changes: 18 additions & 0 deletions pkg/interfaces/mocks/ApplicationService.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions pkg/interfaces/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package interfaces

import (
"context"
"net/http"
"time"

Expand Down Expand Up @@ -75,6 +76,9 @@ type UpdatableConfig interface {

// ApplicationService defines the interface for an edgex Application Service
type ApplicationService interface {
// AppContext returns the application service context used to detect cancelled context when the service is terminating.
// Used by custom app service to appropriately exit any long-running functions.
AppContext() context.Context
// AddRoute a custom REST route to the application service's internal webserver
// A reference to this ApplicationService is add the the context that is passed to the handler, which
// can be retrieved using the `AppService` key
Expand Down

0 comments on commit 5ed1430

Please sign in to comment.