From 0b4c4ecb52b1c04037a65644dc8c6c29981d9736 Mon Sep 17 00:00:00 2001 From: elinuxhenrik Date: Wed, 23 Nov 2022 18:55:03 +0100 Subject: [PATCH 1/1] Add get all published services When an invoker is registered the ApiList is now populated by the invoker manager. Issue-ID: NONRTRIC-814 Signed-off-by: elinuxhenrik Change-Id: I20464ce975c2f5ae9e4506d46ee2bfab43f25cd2 --- .../invokermanagement/invokermanagement.go | 13 ++++ .../invokermanagement/invokermanagement_test.go | 75 ++++++++++++---------- .../publishservice/mocks/PublishRegister.go | 16 +++++ .../internal/publishservice/publishservice.go | 60 +++++++++++------ .../internal/publishservice/publishservice_test.go | 17 +++++ 5 files changed, 127 insertions(+), 54 deletions(-) diff --git a/capifcore/internal/invokermanagement/invokermanagement.go b/capifcore/internal/invokermanagement/invokermanagement.go index 7f5782e..69d7149 100644 --- a/capifcore/internal/invokermanagement/invokermanagement.go +++ b/capifcore/internal/invokermanagement/invokermanagement.go @@ -39,8 +39,14 @@ import ( //go:generate mockery --name InvokerRegister type InvokerRegister interface { + // Checks if the invoker is registered. + // Returns true of the provided invoker is registered, false otherwise. IsInvokerRegistered(invokerId string) bool + // Verifies that the provided secret is the invoker's registered secret. + // Returns true if the provided secret is the registered invoker's secret, false otherwise. VerifyInvokerSecret(invokerId, secret string) bool + // Gets the provided invoker's registered APIs. + // Returns a list of all the invoker's registered APIs. GetInvokerApiList(invokerId string) *invokerapi.APIList } @@ -51,6 +57,7 @@ type InvokerManager struct { lock sync.Mutex } +// Creates a manager that implements both the InvokerRegister and the invokermanagementapi.ServerInterface interfaces. func NewInvokerManager(publishRegister publishservice.PublishRegister) *InvokerManager { return &InvokerManager{ onboardedInvokers: make(map[string]invokerapi.APIInvokerEnrolmentDetails), @@ -86,6 +93,7 @@ func (im *InvokerManager) GetInvokerApiList(invokerId string) *invokerapi.APILis return nil } +// Creates a new individual API Invoker profile. func (im *InvokerManager) PostOnboardedInvokers(ctx echo.Context) error { var newInvoker invokerapi.APIInvokerEnrolmentDetails err := ctx.Bind(&newInvoker) @@ -110,6 +118,9 @@ func (im *InvokerManager) PostOnboardedInvokers(ctx echo.Context) error { } newInvoker.OnboardingInformation.OnboardingSecret = &onboardingSecret + var apiList invokerapi.APIList = im.publishRegister.GetAllPublishedServices() + newInvoker.ApiList = &apiList + im.onboardedInvokers[*newInvoker.ApiInvokerId] = newInvoker uri := ctx.Request().Host + ctx.Request().URL.String() @@ -123,6 +134,7 @@ func (im *InvokerManager) PostOnboardedInvokers(ctx echo.Context) error { return nil } +// Deletes an individual API Invoker. func (im *InvokerManager) DeleteOnboardedInvokersOnboardingId(ctx echo.Context, onboardingId string) error { im.lock.Lock() defer im.lock.Unlock() @@ -132,6 +144,7 @@ func (im *InvokerManager) DeleteOnboardedInvokersOnboardingId(ctx echo.Context, return ctx.NoContent(http.StatusNoContent) } +// Updates an individual API invoker details. func (im *InvokerManager) PutOnboardedInvokersOnboardingId(ctx echo.Context, onboardingId string) error { var invoker invokerapi.APIInvokerEnrolmentDetails err := ctx.Bind(&invoker) diff --git a/capifcore/internal/invokermanagement/invokermanagement_test.go b/capifcore/internal/invokermanagement/invokermanagement_test.go index 365a7d0..0b90de8 100644 --- a/capifcore/internal/invokermanagement/invokermanagement_test.go +++ b/capifcore/internal/invokermanagement/invokermanagement_test.go @@ -41,26 +41,25 @@ import ( "github.com/deepmap/oapi-codegen/pkg/testutil" echomiddleware "github.com/labstack/echo/v4/middleware" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" ) func TestOnboardInvoker(t *testing.T) { - publishRegisterMock := publishmocks.PublishRegister{} - publishRegisterMock.On("AreAPIsPublished", mock.Anything).Return(true) - invokerUnderTest, requestHandler := getEcho(&publishRegisterMock) - aefProfiles := []publishserviceapi.AefProfile{ getAefProfile("aefId"), } apiId := "apiId" - var apiList invokermanagementapi.APIList = []publishserviceapi.ServiceAPIDescription{ + publishedServices := []publishserviceapi.ServiceAPIDescription{ { ApiId: &apiId, AefProfiles: &aefProfiles, }, } + publishRegisterMock := publishmocks.PublishRegister{} + publishRegisterMock.On("GetAllPublishedServices").Return(publishedServices) + invokerUnderTest, requestHandler := getEcho(&publishRegisterMock) + invokerInfo := "invoker a" - newInvoker := getInvoker(invokerInfo, apiList) + newInvoker := getInvoker(invokerInfo) // Onboard a valid invoker result := testutil.NewRequest().Post("/onboardedInvokers").WithJsonBody(newInvoker).Go(t, requestHandler) @@ -78,7 +77,8 @@ func TestOnboardInvoker(t *testing.T) { assert.Equal(t, "http://example.com/onboardedInvokers/"+*resultInvoker.ApiInvokerId, result.Recorder.Header().Get(echo.HeaderLocation)) assert.True(t, invokerUnderTest.IsInvokerRegistered(wantedInvokerId)) assert.True(t, invokerUnderTest.VerifyInvokerSecret(wantedInvokerId, wantedInvokerSecret)) - publishRegisterMock.AssertCalled(t, "AreAPIsPublished", mock.Anything) + publishRegisterMock.AssertCalled(t, "GetAllPublishedServices") + assert.Equal(t, invokermanagementapi.APIList(publishedServices), *resultInvoker.ApiList) // Onboard an invoker missing required NotificationDestination, should get 400 with problem details invalidInvoker := invokermanagementapi.APIInvokerEnrolmentDetails{ @@ -113,7 +113,9 @@ func TestOnboardInvoker(t *testing.T) { } func TestDeleteInvoker(t *testing.T) { - invokerUnderTest, requestHandler := getEcho(nil) + publishRegisterMock := publishmocks.PublishRegister{} + publishRegisterMock.On("GetAllPublishedServices").Return([]publishserviceapi.ServiceAPIDescription{}) + invokerUnderTest, requestHandler := getEcho(&publishRegisterMock) newInvoker := invokermanagementapi.APIInvokerEnrolmentDetails{ NotificationDestination: "url", @@ -136,7 +138,9 @@ func TestDeleteInvoker(t *testing.T) { } func TestUpdateInvoker(t *testing.T) { - _, requestHandler := getEcho(nil) + publishRegisterMock := publishmocks.PublishRegister{} + publishRegisterMock.On("GetAllPublishedServices").Return([]publishserviceapi.ServiceAPIDescription{}) + _, requestHandler := getEcho(&publishRegisterMock) newInvoker := invokermanagementapi.APIInvokerEnrolmentDetails{ NotificationDestination: "url", @@ -150,18 +154,22 @@ func TestUpdateInvoker(t *testing.T) { var resultInvoker invokermanagementapi.APIInvokerEnrolmentDetails result.UnmarshalBodyToObject(&resultInvoker) + // Update the invoker with valid invoker, should return 200 with updated invoker details invokerId := resultInvoker.ApiInvokerId invokerUrl := result.Recorder.Header().Get(echo.HeaderLocation) - - // Update the invoker with valid invoker, should return 200 with invoker details + resultInvoker.ApiList = nil + newNotifURL := "newUrl" + resultInvoker.NotificationDestination = common29122.Uri(newNotifURL) + newPublicKey := "newPublicKey" + resultInvoker.OnboardingInformation.ApiInvokerPublicKey = newPublicKey result = testutil.NewRequest().Put(invokerUrl).WithJsonBody(resultInvoker).Go(t, requestHandler) assert.Equal(t, http.StatusOK, result.Code()) err := result.UnmarshalBodyToObject(&resultInvoker) assert.NoError(t, err, "error unmarshaling response") assert.Equal(t, invokerId, resultInvoker.ApiInvokerId) - assert.Equal(t, newInvoker.NotificationDestination, resultInvoker.NotificationDestination) - assert.Equal(t, newInvoker.OnboardingInformation.ApiInvokerPublicKey, resultInvoker.OnboardingInformation.ApiInvokerPublicKey) + assert.Equal(t, newNotifURL, string(resultInvoker.NotificationDestination)) + assert.Equal(t, newPublicKey, resultInvoker.OnboardingInformation.ApiInvokerPublicKey) // Update with an invoker missing required NotificationDestination, should get 400 with problem details validOnboardingInfo := invokermanagementapi.OnboardingInformation{ @@ -222,35 +230,32 @@ func TestUpdateInvoker(t *testing.T) { } func TestGetInvokerApiList(t *testing.T) { - publishRegisterMock := publishmocks.PublishRegister{} - publishRegisterMock.On("AreAPIsPublished", mock.Anything).Return(true) - invokerUnderTest, requestHandler := getEcho(&publishRegisterMock) - - // Onboard two invokers - aefProfiles := []publishserviceapi.AefProfile{ + aefProfiles1 := []publishserviceapi.AefProfile{ getAefProfile("aefId"), } apiId := "apiId" - var apiList invokermanagementapi.APIList = []publishserviceapi.ServiceAPIDescription{ + apiList := []publishserviceapi.ServiceAPIDescription{ { ApiId: &apiId, - AefProfiles: &aefProfiles, + AefProfiles: &aefProfiles1, }, } - invokerInfo := "invoker a" - newInvoker := getInvoker(invokerInfo, apiList) - testutil.NewRequest().Post("/onboardedInvokers").WithJsonBody(newInvoker).Go(t, requestHandler) - aefProfiles = []publishserviceapi.AefProfile{ + aefProfiles2 := []publishserviceapi.AefProfile{ getAefProfile("aefId2"), } apiId2 := "apiId2" - apiList = []publishserviceapi.ServiceAPIDescription{ - { - ApiId: &apiId2, - AefProfiles: &aefProfiles, - }, - } - newInvoker = getInvoker("invoker b", apiList) + apiList = append(apiList, publishserviceapi.ServiceAPIDescription{ + ApiId: &apiId2, + AefProfiles: &aefProfiles2, + }) + publishRegisterMock := publishmocks.PublishRegister{} + publishRegisterMock.On("GetAllPublishedServices").Return(apiList) + invokerUnderTest, requestHandler := getEcho(&publishRegisterMock) + + invokerInfo := "invoker a" + newInvoker := getInvoker(invokerInfo) + testutil.NewRequest().Post("/onboardedInvokers").WithJsonBody(newInvoker).Go(t, requestHandler) + newInvoker = getInvoker("invoker b") testutil.NewRequest().Post("/onboardedInvokers").WithJsonBody(newInvoker).Go(t, requestHandler) wantedApiList := invokerUnderTest.GetInvokerApiList("api_invoker_id_" + strings.Replace(invokerInfo, " ", "_", 1)) @@ -292,14 +297,14 @@ func getAefProfile(aefId string) publishserviceapi.AefProfile { } } -func getInvoker(invokerInfo string, apiList invokermanagementapi.APIList) invokermanagementapi.APIInvokerEnrolmentDetails { +func getInvoker(invokerInfo string) invokermanagementapi.APIInvokerEnrolmentDetails { newInvoker := invokermanagementapi.APIInvokerEnrolmentDetails{ ApiInvokerInformation: &invokerInfo, NotificationDestination: "url", OnboardingInformation: invokermanagementapi.OnboardingInformation{ ApiInvokerPublicKey: "key", }, - ApiList: &apiList, + ApiList: nil, } return newInvoker } diff --git a/capifcore/internal/publishservice/mocks/PublishRegister.go b/capifcore/internal/publishservice/mocks/PublishRegister.go index b264825..a798a71 100644 --- a/capifcore/internal/publishservice/mocks/PublishRegister.go +++ b/capifcore/internal/publishservice/mocks/PublishRegister.go @@ -27,6 +27,22 @@ func (_m *PublishRegister) AreAPIsPublished(serviceDescriptions *[]publishservic return r0 } +// GetAllPublishedServices provides a mock function with given fields: +func (_m *PublishRegister) GetAllPublishedServices() []publishserviceapi.ServiceAPIDescription { + ret := _m.Called() + + var r0 []publishserviceapi.ServiceAPIDescription + if rf, ok := ret.Get(0).(func() []publishserviceapi.ServiceAPIDescription); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]publishserviceapi.ServiceAPIDescription) + } + } + + return r0 +} + // IsAPIPublished provides a mock function with given fields: aefId, path func (_m *PublishRegister) IsAPIPublished(aefId string, path string) bool { ret := _m.Called(aefId, path) diff --git a/capifcore/internal/publishservice/publishservice.go b/capifcore/internal/publishservice/publishservice.go index 2b7f52d..2314039 100644 --- a/capifcore/internal/publishservice/publishservice.go +++ b/capifcore/internal/publishservice/publishservice.go @@ -31,7 +31,7 @@ import ( "k8s.io/utils/strings/slices" "oransc.org/nonrtric/capifcore/internal/common29122" - "oransc.org/nonrtric/capifcore/internal/publishserviceapi" + publishapi "oransc.org/nonrtric/capifcore/internal/publishserviceapi" "oransc.org/nonrtric/capifcore/internal/helmmanagement" "oransc.org/nonrtric/capifcore/internal/providermanagement" @@ -41,26 +41,34 @@ import ( //go:generate mockery --name PublishRegister type PublishRegister interface { - AreAPIsPublished(serviceDescriptions *[]publishserviceapi.ServiceAPIDescription) bool + // Checks if the provided APIs are published. + // Returns true if all provided APIs have been published, false otherwise. + AreAPIsPublished(serviceDescriptions *[]publishapi.ServiceAPIDescription) bool + // Checks if the provided API is published. + // Returns true if the provided API has been published, false otherwise. IsAPIPublished(aefId, path string) bool + // Gets all published APIs. + // Returns a list of all APIs that has been published. + GetAllPublishedServices() []publishapi.ServiceAPIDescription } type PublishService struct { - publishedServices map[string][]*publishserviceapi.ServiceAPIDescription + publishedServices map[string][]publishapi.ServiceAPIDescription serviceRegister providermanagement.ServiceRegister helmManager helmmanagement.HelmManager lock sync.Mutex } +// Creates a service that implements both the PublishRegister and the publishserviceapi.ServerInterface interfaces. func NewPublishService(serviceRegister providermanagement.ServiceRegister, hm helmmanagement.HelmManager) *PublishService { return &PublishService{ helmManager: hm, - publishedServices: make(map[string][]*publishserviceapi.ServiceAPIDescription), + publishedServices: make(map[string][]publishapi.ServiceAPIDescription), serviceRegister: serviceRegister, } } -func (ps *PublishService) AreAPIsPublished(serviceDescriptions *[]publishserviceapi.ServiceAPIDescription) bool { +func (ps *PublishService) AreAPIsPublished(serviceDescriptions *[]publishapi.ServiceAPIDescription) bool { if serviceDescriptions != nil { registeredApis := ps.getAllAefIds() @@ -76,13 +84,13 @@ func (ps *PublishService) getAllAefIds() []string { allIds := []string{} for _, descriptions := range ps.publishedServices { for _, description := range descriptions { - allIds = append(allIds, getIdsFromDescription(*description)...) + allIds = append(allIds, getIdsFromDescription(description)...) } } return allIds } -func getIdsFromDescription(description publishserviceapi.ServiceAPIDescription) []string { +func getIdsFromDescription(description publishapi.ServiceAPIDescription) []string { allIds := []string{} if description.AefProfiles != nil { for _, aefProfile := range *description.AefProfiles { @@ -92,7 +100,7 @@ func getIdsFromDescription(description publishserviceapi.ServiceAPIDescription) return allIds } -func checkNewDescriptions(newDescriptions []publishserviceapi.ServiceAPIDescription, registeredAefIds []string) bool { +func checkNewDescriptions(newDescriptions []publishapi.ServiceAPIDescription, registeredAefIds []string) bool { registered := true for _, newApi := range newDescriptions { if !checkProfiles(newApi.AefProfiles, registeredAefIds) { @@ -103,7 +111,7 @@ func checkNewDescriptions(newDescriptions []publishserviceapi.ServiceAPIDescript return registered } -func checkProfiles(newProfiles *[]publishserviceapi.AefProfile, registeredAefIds []string) bool { +func checkProfiles(newProfiles *[]publishapi.AefProfile, registeredAefIds []string) bool { allRegistered := true if newProfiles != nil { for _, profile := range *newProfiles { @@ -120,6 +128,15 @@ func (ps *PublishService) IsAPIPublished(aefId, path string) bool { return slices.Contains(ps.getAllAefIds(), aefId) } +func (ps *PublishService) GetAllPublishedServices() []publishapi.ServiceAPIDescription { + publishedDescriptions := []publishapi.ServiceAPIDescription{} + for _, descriptions := range ps.publishedServices { + publishedDescriptions = append(publishedDescriptions, descriptions...) + } + return publishedDescriptions +} + +// Retrieve all published APIs. func (ps *PublishService) GetApfIdServiceApis(ctx echo.Context, apfId string) error { serviceDescriptions, ok := ps.publishedServices[apfId] if ok { @@ -135,8 +152,9 @@ func (ps *PublishService) GetApfIdServiceApis(ctx echo.Context, apfId string) er return nil } +// Publish a new API. func (ps *PublishService) PostApfIdServiceApis(ctx echo.Context, apfId string) error { - var newServiceAPIDescription publishserviceapi.ServiceAPIDescription + var newServiceAPIDescription publishapi.ServiceAPIDescription err := ctx.Bind(&newServiceAPIDescription) if err != nil { return sendCoreError(ctx, http.StatusBadRequest, "Invalid format for service "+apfId) @@ -162,9 +180,9 @@ func (ps *PublishService) PostApfIdServiceApis(ctx echo.Context, apfId string) e _, ok := ps.publishedServices[apfId] if ok { - ps.publishedServices[apfId] = append(ps.publishedServices[apfId], &newServiceAPIDescription) + ps.publishedServices[apfId] = append(ps.publishedServices[apfId], newServiceAPIDescription) } else { - ps.publishedServices[apfId] = append([]*publishserviceapi.ServiceAPIDescription{}, &newServiceAPIDescription) + ps.publishedServices[apfId] = append([]publishapi.ServiceAPIDescription{}, newServiceAPIDescription) } uri := ctx.Request().Host + ctx.Request().URL.String() @@ -178,7 +196,7 @@ func (ps *PublishService) PostApfIdServiceApis(ctx echo.Context, apfId string) e return nil } -func (ps *PublishService) installHelmChart(newServiceAPIDescription publishserviceapi.ServiceAPIDescription, ctx echo.Context) (bool, error) { +func (ps *PublishService) installHelmChart(newServiceAPIDescription publishapi.ServiceAPIDescription, ctx echo.Context) (bool, error) { info := strings.Split(*newServiceAPIDescription.Description, ",") if len(info) == 5 { err := ps.helmManager.InstallHelmChart(info[1], info[2], info[3], info[4]) @@ -190,6 +208,7 @@ func (ps *PublishService) installHelmChart(newServiceAPIDescription publishservi return false, nil } +// Unpublish a published service API. func (ps *PublishService) DeleteApfIdServiceApisServiceApiId(ctx echo.Context, apfId string, serviceApiId string) error { serviceDescriptions, ok := ps.publishedServices[string(apfId)] if ok { @@ -208,6 +227,7 @@ func (ps *PublishService) DeleteApfIdServiceApisServiceApiId(ctx echo.Context, a return ctx.NoContent(http.StatusNoContent) } +// Retrieve a published service API. func (ps *PublishService) GetApfIdServiceApisServiceApiId(ctx echo.Context, apfId string, serviceApiId string) error { ps.lock.Lock() defer ps.lock.Unlock() @@ -229,26 +249,28 @@ func (ps *PublishService) GetApfIdServiceApisServiceApiId(ctx echo.Context, apfI return ctx.NoContent(http.StatusNotFound) } -func getServiceDescription(serviceApiId string, descriptions []*publishserviceapi.ServiceAPIDescription) (int, *publishserviceapi.ServiceAPIDescription) { +func getServiceDescription(serviceApiId string, descriptions []publishapi.ServiceAPIDescription) (int, *publishapi.ServiceAPIDescription) { for pos, description := range descriptions { if serviceApiId == *description.ApiId { - return pos, description + return pos, &description } } return -1, nil } -func removeServiceDescription(i int, a []*publishserviceapi.ServiceAPIDescription) []*publishserviceapi.ServiceAPIDescription { - a[i] = a[len(a)-1] // Copy last element to index i. - a[len(a)-1] = nil // Erase last element (write zero value). - a = a[:len(a)-1] // Truncate slice. +func removeServiceDescription(i int, a []publishapi.ServiceAPIDescription) []publishapi.ServiceAPIDescription { + a[i] = a[len(a)-1] // Copy last element to index i. + a[len(a)-1] = publishapi.ServiceAPIDescription{} // Erase last element (write zero value). + a = a[:len(a)-1] // Truncate slice. return a } +// Modify an existing published service API. func (ps *PublishService) ModifyIndAPFPubAPI(ctx echo.Context, apfId string, serviceApiId string) error { return ctx.NoContent(http.StatusNotImplemented) } +// Update a published service API. func (ps *PublishService) PutApfIdServiceApisServiceApiId(ctx echo.Context, apfId string, serviceApiId string) error { return ctx.NoContent(http.StatusNotImplemented) } diff --git a/capifcore/internal/publishservice/publishservice_test.go b/capifcore/internal/publishservice/publishservice_test.go index 9bb02b6..ab92363 100644 --- a/capifcore/internal/publishservice/publishservice_test.go +++ b/capifcore/internal/publishservice/publishservice_test.go @@ -162,6 +162,23 @@ func TestGetServices(t *testing.T) { assert.Contains(t, resultServices, serviceDescription2) } +func TestGetPublishedServices(t *testing.T) { + serviceUnderTest := NewPublishService(nil, nil) + + profiles := make([]publishapi.AefProfile, 1) + serviceDescription := publishapi.ServiceAPIDescription{ + AefProfiles: &profiles, + } + serviceUnderTest.publishedServices["publisher1"] = []publishapi.ServiceAPIDescription{ + serviceDescription, + } + serviceUnderTest.publishedServices["publisher2"] = []publishapi.ServiceAPIDescription{ + serviceDescription, + } + result := serviceUnderTest.GetAllPublishedServices() + assert.Len(t, result, 2) +} + func getEcho(serviceRegister providermanagement.ServiceRegister, helmManager helmmanagement.HelmManager) (*PublishService, *echo.Echo) { swagger, err := publishapi.GetSwagger() if err != nil { -- 2.16.6