2 // ========================LICENSE_START=================================
5 // Copyright (C) 2022: Nordix Foundation
7 // Licensed under the Apache License, Version 2.0 (the "License");
8 // you may not use this file except in compliance with the License.
9 // You may obtain a copy of the License at
11 // http://www.apache.org/licenses/LICENSE-2.0
13 // Unless required by applicable law or agreed to in writing, software
14 // distributed under the License is distributed on an "AS IS" BASIS,
15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 // See the License for the specific language governing permissions and
17 // limitations under the License.
18 // ========================LICENSE_END===================================
21 package publishservice
29 "github.com/labstack/echo/v4"
30 "k8s.io/utils/strings/slices"
32 "oransc.org/nonrtric/capifcore/internal/common29122"
33 "oransc.org/nonrtric/capifcore/internal/publishserviceapi"
35 "oransc.org/nonrtric/capifcore/internal/helmmanagement"
36 "oransc.org/nonrtric/capifcore/internal/providermanagement"
38 log "github.com/sirupsen/logrus"
41 //go:generate mockery --name PublishRegister
42 type PublishRegister interface {
43 AreAPIsPublished(serviceDescriptions *[]publishserviceapi.ServiceAPIDescription) bool
44 IsAPIPublished(aefId, path string) bool
47 type PublishService struct {
48 publishedServices map[string][]*publishserviceapi.ServiceAPIDescription
49 serviceRegister providermanagement.ServiceRegister
50 helmManager helmmanagement.HelmManager
54 func NewPublishService(serviceRegister providermanagement.ServiceRegister, hm helmmanagement.HelmManager) *PublishService {
55 return &PublishService{
57 publishedServices: make(map[string][]*publishserviceapi.ServiceAPIDescription),
58 serviceRegister: serviceRegister,
62 func (ps *PublishService) AreAPIsPublished(serviceDescriptions *[]publishserviceapi.ServiceAPIDescription) bool {
64 if serviceDescriptions != nil {
65 registeredApis := ps.getAllAefIds()
66 return checkNewDescriptions(*serviceDescriptions, registeredApis)
71 func (ps *PublishService) getAllAefIds() []string {
73 defer ps.lock.Unlock()
76 for _, descriptions := range ps.publishedServices {
77 for _, description := range descriptions {
78 allIds = append(allIds, getIdsFromDescription(*description)...)
84 func getIdsFromDescription(description publishserviceapi.ServiceAPIDescription) []string {
86 if description.AefProfiles != nil {
87 for _, aefProfile := range *description.AefProfiles {
88 allIds = append(allIds, aefProfile.AefId)
94 func checkNewDescriptions(newDescriptions []publishserviceapi.ServiceAPIDescription, registeredAefIds []string) bool {
96 for _, newApi := range newDescriptions {
97 if !checkProfiles(newApi.AefProfiles, registeredAefIds) {
105 func checkProfiles(newProfiles *[]publishserviceapi.AefProfile, registeredAefIds []string) bool {
106 allRegistered := true
107 if newProfiles != nil {
108 for _, profile := range *newProfiles {
109 if !slices.Contains(registeredAefIds, profile.AefId) {
110 allRegistered = false
118 func (ps *PublishService) IsAPIPublished(aefId, path string) bool {
119 return slices.Contains(ps.getAllAefIds(), aefId)
122 func (ps *PublishService) GetApfIdServiceApis(ctx echo.Context, apfId string) error {
123 return ctx.NoContent(http.StatusNotImplemented)
126 func (ps *PublishService) PostApfIdServiceApis(ctx echo.Context, apfId string) error {
127 var newServiceAPIDescription publishserviceapi.ServiceAPIDescription
128 err := ctx.Bind(&newServiceAPIDescription)
130 return sendCoreError(ctx, http.StatusBadRequest, "Invalid format for service")
134 defer ps.lock.Unlock()
136 registeredFuncs := ps.serviceRegister.GetAefsForPublisher(apfId)
137 for _, profile := range *newServiceAPIDescription.AefProfiles {
138 if !slices.Contains(registeredFuncs, profile.AefId) {
139 return sendCoreError(ctx, http.StatusNotFound, "Function not registered, "+profile.AefId)
143 newId := "api_id_" + newServiceAPIDescription.ApiName
144 newServiceAPIDescription.ApiId = &newId
146 shouldReturn, returnValue := ps.installHelmChart(newServiceAPIDescription, err, ctx, newId)
151 _, ok := ps.publishedServices[apfId]
153 ps.publishedServices[apfId] = append(ps.publishedServices[apfId], &newServiceAPIDescription)
155 ps.publishedServices[apfId] = append([]*publishserviceapi.ServiceAPIDescription{}, &newServiceAPIDescription)
158 uri := ctx.Request().Host + ctx.Request().URL.String()
159 ctx.Response().Header().Set(echo.HeaderLocation, ctx.Scheme()+`://`+path.Join(uri, *newServiceAPIDescription.ApiId))
160 err = ctx.JSON(http.StatusCreated, newServiceAPIDescription)
162 // Something really bad happened, tell Echo that our handler failed
169 func (ps *PublishService) installHelmChart(newServiceAPIDescription publishserviceapi.ServiceAPIDescription, err error, ctx echo.Context, newId string) (bool, error) {
170 info := strings.Split(*newServiceAPIDescription.Description, ",")
172 err = ps.helmManager.InstallHelmChart(info[1], info[2], info[3], info[4])
174 return true, sendCoreError(ctx, http.StatusBadRequest, "Unable to install Helm chart due to: "+err.Error())
176 log.Info("Installed service: ", newId)
181 func (ps *PublishService) DeleteApfIdServiceApisServiceApiId(ctx echo.Context, apfId string, serviceApiId string) error {
182 serviceDescriptions, ok := ps.publishedServices[string(apfId)]
184 pos, description := getServiceDescription(serviceApiId, serviceDescriptions)
185 if description != nil {
186 info := strings.Split(*description.Description, ",")
188 ps.helmManager.UninstallHelmChart(info[1], info[3])
189 log.Info("Deleted service: ", serviceApiId)
192 defer ps.lock.Unlock()
193 ps.publishedServices[string(apfId)] = removeServiceDescription(pos, serviceDescriptions)
196 return ctx.NoContent(http.StatusNoContent)
199 func (ps *PublishService) GetApfIdServiceApisServiceApiId(ctx echo.Context, apfId string, serviceApiId string) error {
201 defer ps.lock.Unlock()
203 serviceDescriptions, ok := ps.publishedServices[apfId]
205 _, serviceDescription := getServiceDescription(serviceApiId, serviceDescriptions)
206 if serviceDescription == nil {
207 return ctx.NoContent(http.StatusNotFound)
209 err := ctx.JSON(http.StatusOK, serviceDescription)
211 // Something really bad happened, tell Echo that our handler failed
217 return ctx.NoContent(http.StatusNotFound)
220 func getServiceDescription(serviceApiId string, descriptions []*publishserviceapi.ServiceAPIDescription) (int, *publishserviceapi.ServiceAPIDescription) {
221 for pos, description := range descriptions {
222 if serviceApiId == *description.ApiId {
223 return pos, description
229 func removeServiceDescription(i int, a []*publishserviceapi.ServiceAPIDescription) []*publishserviceapi.ServiceAPIDescription {
230 a[i] = a[len(a)-1] // Copy last element to index i.
231 a[len(a)-1] = nil // Erase last element (write zero value).
232 a = a[:len(a)-1] // Truncate slice.
236 func (ps *PublishService) ModifyIndAPFPubAPI(ctx echo.Context, apfId string, serviceApiId string) error {
237 return ctx.NoContent(http.StatusNotImplemented)
240 func (ps *PublishService) PutApfIdServiceApisServiceApiId(ctx echo.Context, apfId string, serviceApiId string) error {
241 return ctx.NoContent(http.StatusNotImplemented)
244 // This function wraps sending of an error in the Error format, and
245 // handling the failure to marshal that.
246 func sendCoreError(ctx echo.Context, code int, message string) error {
247 pd := common29122.ProblemDetails{
251 err := ctx.JSON(code, pd)