"sync"
"github.com/labstack/echo/v4"
+ "k8s.io/utils/strings/slices"
"oransc.org/nonrtric/capifcore/internal/common29122"
"oransc.org/nonrtric/capifcore/internal/publishserviceapi"
//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
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 {
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)
}
}
}
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))
}
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
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)
}
)
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)
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)
// 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"
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