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
30 "github.com/labstack/echo/v4"
31 "k8s.io/utils/strings/slices"
33 "oransc.org/nonrtric/capifcore/internal/common29122"
34 "oransc.org/nonrtric/capifcore/internal/publishserviceapi"
36 "oransc.org/nonrtric/capifcore/internal/helmmanagement"
37 "oransc.org/nonrtric/capifcore/internal/providermanagement"
39 log "github.com/sirupsen/logrus"
42 //go:generate mockery --name PublishRegister
43 type PublishRegister interface {
44 AreAPIsPublished(serviceDescriptions *[]publishserviceapi.ServiceAPIDescription) bool
45 IsAPIPublished(aefId, path string) bool
48 type PublishService struct {
49 publishedServices map[string][]*publishserviceapi.ServiceAPIDescription
50 serviceRegister providermanagement.ServiceRegister
51 helmManager helmmanagement.HelmManager
55 func NewPublishService(serviceRegister providermanagement.ServiceRegister, hm helmmanagement.HelmManager) *PublishService {
56 return &PublishService{
58 publishedServices: make(map[string][]*publishserviceapi.ServiceAPIDescription),
59 serviceRegister: serviceRegister,
63 func (ps *PublishService) AreAPIsPublished(serviceDescriptions *[]publishserviceapi.ServiceAPIDescription) bool {
65 if serviceDescriptions != nil {
66 registeredApis := ps.getAllAefIds()
67 return checkNewDescriptions(*serviceDescriptions, registeredApis)
72 func (ps *PublishService) getAllAefIds() []string {
74 defer ps.lock.Unlock()
77 for _, descriptions := range ps.publishedServices {
78 for _, description := range descriptions {
79 allIds = append(allIds, getIdsFromDescription(*description)...)
85 func getIdsFromDescription(description publishserviceapi.ServiceAPIDescription) []string {
87 if description.AefProfiles != nil {
88 for _, aefProfile := range *description.AefProfiles {
89 allIds = append(allIds, aefProfile.AefId)
95 func checkNewDescriptions(newDescriptions []publishserviceapi.ServiceAPIDescription, registeredAefIds []string) bool {
97 for _, newApi := range newDescriptions {
98 if !checkProfiles(newApi.AefProfiles, registeredAefIds) {
106 func checkProfiles(newProfiles *[]publishserviceapi.AefProfile, registeredAefIds []string) bool {
107 allRegistered := true
108 if newProfiles != nil {
109 for _, profile := range *newProfiles {
110 if !slices.Contains(registeredAefIds, profile.AefId) {
111 allRegistered = false
119 func (ps *PublishService) IsAPIPublished(aefId, path string) bool {
120 return slices.Contains(ps.getAllAefIds(), aefId)
123 func (ps *PublishService) GetApfIdServiceApis(ctx echo.Context, apfId string) error {
124 serviceDescriptions, ok := ps.publishedServices[apfId]
126 err := ctx.JSON(http.StatusOK, serviceDescriptions)
128 // Something really bad happened, tell Echo that our handler failed
132 return sendCoreError(ctx, http.StatusNotFound, fmt.Sprintf("Provider %s not registered", apfId))
138 func (ps *PublishService) PostApfIdServiceApis(ctx echo.Context, apfId string) error {
139 var newServiceAPIDescription publishserviceapi.ServiceAPIDescription
140 err := ctx.Bind(&newServiceAPIDescription)
142 return sendCoreError(ctx, http.StatusBadRequest, "Invalid format for service "+apfId)
146 defer ps.lock.Unlock()
148 registeredFuncs := ps.serviceRegister.GetAefsForPublisher(apfId)
149 for _, profile := range *newServiceAPIDescription.AefProfiles {
150 if !slices.Contains(registeredFuncs, profile.AefId) {
151 return sendCoreError(ctx, http.StatusNotFound, fmt.Sprintf("Function %s not registered", profile.AefId))
155 newId := "api_id_" + newServiceAPIDescription.ApiName
156 newServiceAPIDescription.ApiId = &newId
158 shouldReturn, returnValue := ps.installHelmChart(newServiceAPIDescription, ctx)
163 _, ok := ps.publishedServices[apfId]
165 ps.publishedServices[apfId] = append(ps.publishedServices[apfId], &newServiceAPIDescription)
167 ps.publishedServices[apfId] = append([]*publishserviceapi.ServiceAPIDescription{}, &newServiceAPIDescription)
170 uri := ctx.Request().Host + ctx.Request().URL.String()
171 ctx.Response().Header().Set(echo.HeaderLocation, ctx.Scheme()+`://`+path.Join(uri, *newServiceAPIDescription.ApiId))
172 err = ctx.JSON(http.StatusCreated, newServiceAPIDescription)
174 // Something really bad happened, tell Echo that our handler failed
181 func (ps *PublishService) installHelmChart(newServiceAPIDescription publishserviceapi.ServiceAPIDescription, ctx echo.Context) (bool, error) {
182 info := strings.Split(*newServiceAPIDescription.Description, ",")
184 err := ps.helmManager.InstallHelmChart(info[1], info[2], info[3], info[4])
186 return true, sendCoreError(ctx, http.StatusBadRequest, fmt.Sprintf("Unable to install Helm chart %s due to: %s", info[3], err.Error()))
188 log.Debug("Installed service: ", newServiceAPIDescription.ApiId)
193 func (ps *PublishService) DeleteApfIdServiceApisServiceApiId(ctx echo.Context, apfId string, serviceApiId string) error {
194 serviceDescriptions, ok := ps.publishedServices[string(apfId)]
196 pos, description := getServiceDescription(serviceApiId, serviceDescriptions)
197 if description != nil {
198 info := strings.Split(*description.Description, ",")
200 ps.helmManager.UninstallHelmChart(info[1], info[3])
201 log.Debug("Deleted service: ", serviceApiId)
204 defer ps.lock.Unlock()
205 ps.publishedServices[string(apfId)] = removeServiceDescription(pos, serviceDescriptions)
208 return ctx.NoContent(http.StatusNoContent)
211 func (ps *PublishService) GetApfIdServiceApisServiceApiId(ctx echo.Context, apfId string, serviceApiId string) error {
213 defer ps.lock.Unlock()
215 serviceDescriptions, ok := ps.publishedServices[apfId]
217 _, serviceDescription := getServiceDescription(serviceApiId, serviceDescriptions)
218 if serviceDescription == nil {
219 return ctx.NoContent(http.StatusNotFound)
221 err := ctx.JSON(http.StatusOK, serviceDescription)
223 // Something really bad happened, tell Echo that our handler failed
229 return ctx.NoContent(http.StatusNotFound)
232 func getServiceDescription(serviceApiId string, descriptions []*publishserviceapi.ServiceAPIDescription) (int, *publishserviceapi.ServiceAPIDescription) {
233 for pos, description := range descriptions {
234 if serviceApiId == *description.ApiId {
235 return pos, description
241 func removeServiceDescription(i int, a []*publishserviceapi.ServiceAPIDescription) []*publishserviceapi.ServiceAPIDescription {
242 a[i] = a[len(a)-1] // Copy last element to index i.
243 a[len(a)-1] = nil // Erase last element (write zero value).
244 a = a[:len(a)-1] // Truncate slice.
248 func (ps *PublishService) ModifyIndAPFPubAPI(ctx echo.Context, apfId string, serviceApiId string) error {
249 return ctx.NoContent(http.StatusNotImplemented)
252 func (ps *PublishService) PutApfIdServiceApisServiceApiId(ctx echo.Context, apfId string, serviceApiId string) error {
253 return ctx.NoContent(http.StatusNotImplemented)
256 // This function wraps sending of an error in the Error format, and
257 // handling the failure to marshal that.
258 func sendCoreError(ctx echo.Context, code int, message string) error {
259 pd := common29122.ProblemDetails{
263 err := ctx.JSON(code, pd)