From f083d47095bf223383a4e6c17d895655b900f9de Mon Sep 17 00:00:00 2001 From: elinuxhenrik Date: Mon, 14 Nov 2022 14:00:14 +0100 Subject: [PATCH 1/1] Use apfId in publish service Issue-ID: NONRTRIC-814 Signed-off-by: elinuxhenrik Change-Id: I288b27fcbb07ecf8fa06036dc656db5f4ba0016c --- .../internal/publishservice/mocks/APIRegister.go | 16 --- .../internal/publishservice/publishservice.go | 152 ++++++++++++--------- .../internal/publishservice/publishservice_test.go | 24 ++-- 3 files changed, 99 insertions(+), 93 deletions(-) diff --git a/capifcore/internal/publishservice/mocks/APIRegister.go b/capifcore/internal/publishservice/mocks/APIRegister.go index 860c125..0ee0a14 100644 --- a/capifcore/internal/publishservice/mocks/APIRegister.go +++ b/capifcore/internal/publishservice/mocks/APIRegister.go @@ -27,22 +27,6 @@ func (_m *APIRegister) AreAPIsRegistered(serviceDescriptions *[]publishserviceap return r0 } -// GetAPIs provides a mock function with given fields: -func (_m *APIRegister) GetAPIs() *[]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 -} - // IsAPIRegistered provides a mock function with given fields: aefId, path func (_m *APIRegister) IsAPIRegistered(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 43cdb8d..c62e73b 100644 --- a/capifcore/internal/publishservice/publishservice.go +++ b/capifcore/internal/publishservice/publishservice.go @@ -27,6 +27,7 @@ import ( "sync" "github.com/labstack/echo/v4" + "k8s.io/utils/strings/slices" "oransc.org/nonrtric/capifcore/internal/common29122" "oransc.org/nonrtric/capifcore/internal/publishserviceapi" @@ -40,12 +41,11 @@ import ( //go:generate mockery --name APIRegister type APIRegister interface { AreAPIsRegistered(serviceDescriptions *[]publishserviceapi.ServiceAPIDescription) bool - GetAPIs() *[]publishserviceapi.ServiceAPIDescription IsAPIRegistered(aefId, path string) bool } type PublishService struct { - publishedServices map[string]publishserviceapi.ServiceAPIDescription + publishedServices map[string][]*publishserviceapi.ServiceAPIDescription serviceRegister providermanagement.ServiceRegister helmManager helmmanagement.HelmManager lock sync.Mutex @@ -54,81 +54,69 @@ type PublishService struct { func NewPublishService(serviceRegister providermanagement.ServiceRegister, hm helmmanagement.HelmManager) *PublishService { return &PublishService{ helmManager: hm, - publishedServices: make(map[string]publishserviceapi.ServiceAPIDescription), + publishedServices: make(map[string][]*publishserviceapi.ServiceAPIDescription), serviceRegister: serviceRegister, } } func (ps *PublishService) AreAPIsRegistered(serviceDescriptions *[]publishserviceapi.ServiceAPIDescription) bool { + + if serviceDescriptions != nil { + registeredApis := ps.getAllAefIds() + return checkNewDescriptions(*serviceDescriptions, registeredApis) + } + return true +} + +func (ps *PublishService) getAllAefIds() []string { ps.lock.Lock() defer ps.lock.Unlock() - allRegistered := true - if serviceDescriptions != nil { - out: - for _, newApi := range *serviceDescriptions { - registeredApi, ok := ps.publishedServices[*newApi.ApiId] - if ok { - if !ps.areProfilesRegistered(newApi.AefProfiles, registeredApi.AefProfiles) { - allRegistered = false - break out - } - } else { - allRegistered = false - break out - } + allIds := []string{} + for _, descriptions := range ps.publishedServices { + for _, description := range descriptions { + allIds = append(allIds, getIdsFromDescription(*description)...) } } - return allRegistered + return allIds } -func (ps *PublishService) areProfilesRegistered(newProfiles *[]publishserviceapi.AefProfile, registeredProfiles *[]publishserviceapi.AefProfile) bool { - allRegistered := true - if newProfiles != nil && registeredProfiles != nil { - out: - for _, newProfile := range *newProfiles { - for _, registeredProfile := range *registeredProfiles { - if newProfile.AefId == registeredProfile.AefId { - break - } - allRegistered = false - break out - } +func getIdsFromDescription(description publishserviceapi.ServiceAPIDescription) []string { + allIds := []string{} + if description.AefProfiles != nil { + for _, aefProfile := range *description.AefProfiles { + allIds = append(allIds, aefProfile.AefId) } - } else if registeredProfiles == nil { - allRegistered = false } - return allRegistered + return allIds } -func (ps *PublishService) GetAPIs() *[]publishserviceapi.ServiceAPIDescription { - ps.lock.Lock() - defer ps.lock.Unlock() - - apis := []publishserviceapi.ServiceAPIDescription{} - for _, service := range ps.publishedServices { - apis = append(apis, service) +func checkNewDescriptions(newDescriptions []publishserviceapi.ServiceAPIDescription, registeredAefIds []string) bool { + registered := true + for _, newApi := range newDescriptions { + if !checkProfiles(newApi.AefProfiles, registeredAefIds) { + registered = false + break + } } - return &apis + return registered } -func (ps *PublishService) IsAPIRegistered(aefId, path string) bool { - ps.lock.Lock() - defer ps.lock.Unlock() - - registered := false -out: - for _, service := range ps.publishedServices { - if service.ApiName == path { - for _, profile := range *service.AefProfiles { - if profile.AefId == aefId { - registered = true - break out - } +func checkProfiles(newProfiles *[]publishserviceapi.AefProfile, registeredAefIds []string) bool { + allRegistered := true + if newProfiles != nil { + for _, profile := range *newProfiles { + if !slices.Contains(registeredAefIds, profile.AefId) { + allRegistered = false + break } } } - return registered + return allRegistered +} + +func (ps *PublishService) IsAPIRegistered(aefId, path string) bool { + return slices.Contains(ps.getAllAefIds(), aefId) } func (ps *PublishService) GetApfIdServiceApis(ctx echo.Context, apfId string) error { @@ -145,8 +133,9 @@ func (ps *PublishService) PostApfIdServiceApis(ctx echo.Context, apfId string) e ps.lock.Lock() defer ps.lock.Unlock() + registeredFuncs := ps.serviceRegister.GetAefsForPublisher(apfId) for _, profile := range *newServiceAPIDescription.AefProfiles { - if !ps.serviceRegister.IsFunctionRegistered(profile.AefId) { + if !slices.Contains(registeredFuncs, profile.AefId) { return sendCoreError(ctx, http.StatusNotFound, "Function not registered, "+profile.AefId) } } @@ -161,7 +150,12 @@ func (ps *PublishService) PostApfIdServiceApis(ctx echo.Context, apfId string) e } log.Info("Installed service: ", newId) } - ps.publishedServices[*newServiceAPIDescription.ApiId] = newServiceAPIDescription + _, ok := ps.publishedServices[apfId] + if ok { + ps.publishedServices[apfId] = append(ps.publishedServices[apfId], &newServiceAPIDescription) + } else { + ps.publishedServices[apfId] = append([]*publishserviceapi.ServiceAPIDescription{}, &newServiceAPIDescription) + } uri := ctx.Request().Host + ctx.Request().URL.String() ctx.Response().Header().Set(echo.HeaderLocation, ctx.Scheme()+`://`+path.Join(uri, *newServiceAPIDescription.ApiId)) @@ -175,23 +169,33 @@ func (ps *PublishService) PostApfIdServiceApis(ctx echo.Context, apfId string) e } func (ps *PublishService) DeleteApfIdServiceApisServiceApiId(ctx echo.Context, apfId string, serviceApiId string) error { - serviceDescription, ok := ps.publishedServices[string(serviceApiId)] + serviceDescriptions, ok := ps.publishedServices[string(apfId)] if ok { - info := strings.Split(*serviceDescription.Description, ",") - if len(info) == 5 { - ps.helmManager.UninstallHelmChart(info[1], info[3]) - log.Info("Deleted service: ", serviceApiId) + pos, description := getServiceDescription(serviceApiId, serviceDescriptions) + if description != nil { + info := strings.Split(*description.Description, ",") + if len(info) == 5 { + ps.helmManager.UninstallHelmChart(info[1], info[3]) + log.Info("Deleted service: ", serviceApiId) + } + ps.lock.Lock() + defer ps.lock.Unlock() + ps.publishedServices[string(apfId)] = removeServiceDescription(pos, serviceDescriptions) } - ps.lock.Lock() - defer ps.lock.Unlock() - delete(ps.publishedServices, string(serviceApiId)) } return ctx.NoContent(http.StatusNoContent) } func (ps *PublishService) GetApfIdServiceApisServiceApiId(ctx echo.Context, apfId string, serviceApiId string) error { - serviceDescription, ok := ps.publishedServices[string(serviceApiId)] + ps.lock.Lock() + defer ps.lock.Unlock() + + serviceDescriptions, ok := ps.publishedServices[apfId] if ok { + _, serviceDescription := getServiceDescription(serviceApiId, serviceDescriptions) + if serviceDescription == nil { + return ctx.NoContent(http.StatusNotFound) + } err := ctx.JSON(http.StatusOK, serviceDescription) if err != nil { // Something really bad happened, tell Echo that our handler failed @@ -203,6 +207,22 @@ func (ps *PublishService) GetApfIdServiceApisServiceApiId(ctx echo.Context, apfI return ctx.NoContent(http.StatusNotFound) } +func getServiceDescription(serviceApiId string, descriptions []*publishserviceapi.ServiceAPIDescription) (int, *publishserviceapi.ServiceAPIDescription) { + for pos, description := range descriptions { + if serviceApiId == *description.ApiId { + 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. + return a +} + func (ps *PublishService) ModifyIndAPFPubAPI(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 a631071..db23ce1 100644 --- a/capifcore/internal/publishservice/publishservice_test.go +++ b/capifcore/internal/publishservice/publishservice_test.go @@ -45,10 +45,11 @@ import ( ) func TestPublishUnpublishService(t *testing.T) { + apfId := "apfId" aefId := "aefId" newApiId := "api_id_app-management" serviceRegisterMock := serviceMocks.ServiceRegister{} - serviceRegisterMock.On("IsFunctionRegistered", aefId).Return(true) + serviceRegisterMock.On("GetAefsForPublisher", apfId).Return([]string{aefId, "otherAefId"}) helmManagerMock := helmMocks.HelmManager{} helmManagerMock.On("InstallHelmChart", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) serviceUnderTest, requestHandler := getEcho(&serviceRegisterMock, &helmManagerMock) @@ -64,24 +65,24 @@ func TestPublishUnpublishService(t *testing.T) { newServiceDescription := getServiceAPIDescription(aefId, domainName, description, protocol) // Publish a service - result = testutil.NewRequest().Post("/aefId/service-apis").WithJsonBody(newServiceDescription).Go(t, requestHandler) + result = testutil.NewRequest().Post("/"+apfId+"/service-apis").WithJsonBody(newServiceDescription).Go(t, requestHandler) assert.Equal(t, http.StatusCreated, result.Code()) var resultService publishapi.ServiceAPIDescription err := result.UnmarshalBodyToObject(&resultService) assert.NoError(t, err, "error unmarshaling response") assert.Equal(t, *resultService.ApiId, newApiId) - assert.Equal(t, "http://example.com/"+aefId+"/service-apis/"+*resultService.ApiId, result.Recorder.Header().Get(echo.HeaderLocation)) + assert.Equal(t, "http://example.com/"+apfId+"/service-apis/"+*resultService.ApiId, result.Recorder.Header().Get(echo.HeaderLocation)) newServiceDescription.ApiId = &newApiId wantedAPILIst := []publishapi.ServiceAPIDescription{newServiceDescription} assert.True(t, serviceUnderTest.AreAPIsRegistered(&wantedAPILIst)) assert.True(t, serviceUnderTest.IsAPIRegistered("aefId", "app-management")) - serviceRegisterMock.AssertCalled(t, "IsFunctionRegistered", aefId) + serviceRegisterMock.AssertCalled(t, "GetAefsForPublisher", apfId) helmManagerMock.AssertCalled(t, "InstallHelmChart", "namespace", "repoName", "chartName", "releaseName") - assert.ElementsMatch(t, wantedAPILIst, *serviceUnderTest.GetAPIs()) + assert.ElementsMatch(t, []string{aefId}, serviceUnderTest.getAllAefIds()) // Check that service is published - result = testutil.NewRequest().Get("/aefId/service-apis/"+newApiId).Go(t, requestHandler) + result = testutil.NewRequest().Get("/"+apfId+"/service-apis/"+newApiId).Go(t, requestHandler) assert.Equal(t, http.StatusOK, result.Code()) err = result.UnmarshalBodyToObject(&resultService) @@ -90,22 +91,23 @@ func TestPublishUnpublishService(t *testing.T) { // Delete a service helmManagerMock.On("UninstallHelmChart", mock.Anything, mock.Anything).Return(nil) - result = testutil.NewRequest().Delete("/aefId/service-apis/"+newApiId).Go(t, requestHandler) + result = testutil.NewRequest().Delete("/"+apfId+"/service-apis/"+newApiId).Go(t, requestHandler) assert.Equal(t, http.StatusNoContent, result.Code()) helmManagerMock.AssertCalled(t, "UninstallHelmChart", "namespace", "chartName") - assert.Empty(t, *serviceUnderTest.GetAPIs()) + assert.Empty(t, serviceUnderTest.getAllAefIds()) // Check no services published - result = testutil.NewRequest().Get("/aefId/service-apis/"+newApiId).Go(t, requestHandler) + result = testutil.NewRequest().Get("/"+apfId+"/service-apis/"+newApiId).Go(t, requestHandler) assert.Equal(t, http.StatusNotFound, result.Code()) } func TestPostUnpublishedServiceWithUnregisteredFunction(t *testing.T) { + apfId := "apfId" aefId := "aefId" serviceRegisterMock := serviceMocks.ServiceRegister{} - serviceRegisterMock.On("IsFunctionRegistered", aefId).Return(false) + serviceRegisterMock.On("GetAefsForPublisher", apfId).Return([]string{"otherAefId"}) _, requestHandler := getEcho(&serviceRegisterMock, nil) domainName := "domain" @@ -114,7 +116,7 @@ func TestPostUnpublishedServiceWithUnregisteredFunction(t *testing.T) { newServiceDescription := getServiceAPIDescription(aefId, domainName, description, protocol) // Publish a service - result := testutil.NewRequest().Post("/aefId/service-apis").WithJsonBody(newServiceDescription).Go(t, requestHandler) + result := testutil.NewRequest().Post("/"+apfId+"/service-apis").WithJsonBody(newServiceDescription).Go(t, requestHandler) assert.Equal(t, http.StatusNotFound, result.Code()) var resultError common29122.ProblemDetails -- 2.16.6