Move capifcore code to separate folder
[nonrtric/plt/sme.git] / capifcore / internal / publishservice / publishservice.go
1 // -
2 //   ========================LICENSE_START=================================
3 //   O-RAN-SC
4 //   %%
5 //   Copyright (C) 2022: Nordix Foundation
6 //   %%
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
10 //
11 //        http://www.apache.org/licenses/LICENSE-2.0
12 //
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===================================
19 //
20
21 package publishservice
22
23 import (
24         "net/http"
25         "path"
26         "strings"
27         "sync"
28
29         "github.com/labstack/echo/v4"
30
31         "oransc.org/nonrtric/capifcore/internal/common29122"
32         "oransc.org/nonrtric/capifcore/internal/publishserviceapi"
33
34         "oransc.org/nonrtric/capifcore/internal/helmmanagement"
35         "oransc.org/nonrtric/capifcore/internal/providermanagement"
36
37         log "github.com/sirupsen/logrus"
38 )
39
40 //go:generate mockery --name APIRegister
41 type APIRegister interface {
42         AreAPIsRegistered(serviceDescriptions *[]publishserviceapi.ServiceAPIDescription) bool
43         GetAPIs() *[]publishserviceapi.ServiceAPIDescription
44         IsAPIRegistered(aefId, path string) bool
45 }
46
47 type PublishService struct {
48         publishedServices map[string]publishserviceapi.ServiceAPIDescription
49         serviceRegister   providermanagement.ServiceRegister
50         helmManager       helmmanagement.HelmManager
51         lock              sync.Mutex
52 }
53
54 func NewPublishService(serviceRegister providermanagement.ServiceRegister, hm helmmanagement.HelmManager) *PublishService {
55         return &PublishService{
56                 helmManager:       hm,
57                 publishedServices: make(map[string]publishserviceapi.ServiceAPIDescription),
58                 serviceRegister:   serviceRegister,
59         }
60 }
61
62 func (ps *PublishService) AreAPIsRegistered(serviceDescriptions *[]publishserviceapi.ServiceAPIDescription) bool {
63         ps.lock.Lock()
64         defer ps.lock.Unlock()
65
66         allRegistered := true
67         if serviceDescriptions != nil {
68         out:
69                 for _, newApi := range *serviceDescriptions {
70                         registeredApi, ok := ps.publishedServices[*newApi.ApiId]
71                         if ok {
72                                 if !ps.areProfilesRegistered(newApi.AefProfiles, registeredApi.AefProfiles) {
73                                         allRegistered = false
74                                         break out
75                                 }
76                         } else {
77                                 allRegistered = false
78                                 break out
79                         }
80                 }
81         }
82         return allRegistered
83 }
84
85 func (ps *PublishService) areProfilesRegistered(newProfiles *[]publishserviceapi.AefProfile, registeredProfiles *[]publishserviceapi.AefProfile) bool {
86         allRegistered := true
87         if newProfiles != nil && registeredProfiles != nil {
88         out:
89                 for _, newProfile := range *newProfiles {
90                         for _, registeredProfile := range *registeredProfiles {
91                                 if newProfile.AefId == registeredProfile.AefId {
92                                         break
93                                 }
94                                 allRegistered = false
95                                 break out
96                         }
97                 }
98         } else if registeredProfiles == nil {
99                 allRegistered = false
100         }
101         return allRegistered
102 }
103
104 func (ps *PublishService) GetAPIs() *[]publishserviceapi.ServiceAPIDescription {
105         ps.lock.Lock()
106         defer ps.lock.Unlock()
107
108         apis := []publishserviceapi.ServiceAPIDescription{}
109         for _, service := range ps.publishedServices {
110                 apis = append(apis, service)
111         }
112         return &apis
113 }
114
115 func (ps *PublishService) IsAPIRegistered(aefId, path string) bool {
116         ps.lock.Lock()
117         defer ps.lock.Unlock()
118
119         registered := false
120 out:
121         for _, service := range ps.publishedServices {
122                 if service.ApiName == path {
123                         for _, profile := range *service.AefProfiles {
124                                 if profile.AefId == aefId {
125                                         registered = true
126                                         break out
127                                 }
128                         }
129                 }
130         }
131         return registered
132 }
133
134 func (ps *PublishService) GetApfIdServiceApis(ctx echo.Context, apfId publishserviceapi.ApfId) error {
135         return ctx.NoContent(http.StatusNotImplemented)
136 }
137
138 func (ps *PublishService) PostApfIdServiceApis(ctx echo.Context, apfId publishserviceapi.ApfId) error {
139         var newServiceAPIDescription publishserviceapi.ServiceAPIDescription
140         err := ctx.Bind(&newServiceAPIDescription)
141         if err != nil {
142                 return sendCoreError(ctx, http.StatusBadRequest, "Invalid format for service")
143         }
144
145         ps.lock.Lock()
146         defer ps.lock.Unlock()
147
148         for _, profile := range *newServiceAPIDescription.AefProfiles {
149                 if !ps.serviceRegister.IsFunctionRegistered(profile.AefId) {
150                         return sendCoreError(ctx, http.StatusNotFound, "Function not registered, "+profile.AefId)
151                 }
152         }
153
154         newId := "api_id_" + newServiceAPIDescription.ApiName
155         newServiceAPIDescription.ApiId = &newId
156         info := strings.Split(*newServiceAPIDescription.Description, ",")
157         if len(info) == 5 {
158                 err = ps.helmManager.InstallHelmChart(info[1], info[2], info[3], info[4])
159                 if err != nil {
160                         return sendCoreError(ctx, http.StatusBadRequest, "Unable to install Helm chart due to: "+err.Error())
161                 }
162                 log.Info("Installed service: ", newId)
163         }
164         ps.publishedServices[*newServiceAPIDescription.ApiId] = newServiceAPIDescription
165
166         uri := ctx.Request().Host + ctx.Request().URL.String()
167         ctx.Response().Header().Set(echo.HeaderLocation, ctx.Scheme()+`://`+path.Join(uri, *newServiceAPIDescription.ApiId))
168         err = ctx.JSON(http.StatusCreated, newServiceAPIDescription)
169         if err != nil {
170                 // Something really bad happened, tell Echo that our handler failed
171                 return err
172         }
173
174         return nil
175 }
176
177 func (ps *PublishService) DeleteApfIdServiceApisServiceApiId(ctx echo.Context, apfId publishserviceapi.ApfId, serviceApiId publishserviceapi.ServiceApiId) error {
178         serviceDescription, ok := ps.publishedServices[string(serviceApiId)]
179         if ok {
180                 info := strings.Split(*serviceDescription.Description, ",")
181                 if len(info) == 5 {
182                         ps.helmManager.UninstallHelmChart(info[1], info[3])
183                         log.Info("Deleted service: ", serviceApiId)
184                 }
185                 delete(ps.publishedServices, string(serviceApiId))
186         }
187         return ctx.NoContent(http.StatusNoContent)
188 }
189
190 func (ps *PublishService) GetApfIdServiceApisServiceApiId(ctx echo.Context, apfId publishserviceapi.ApfId, serviceApiId publishserviceapi.ServiceApiId) error {
191         serviceDescription, ok := ps.publishedServices[string(serviceApiId)]
192         if ok {
193                 err := ctx.JSON(http.StatusOK, serviceDescription)
194                 if err != nil {
195                         // Something really bad happened, tell Echo that our handler failed
196                         return err
197                 }
198
199                 return nil
200         }
201         return ctx.NoContent(http.StatusNotFound)
202 }
203
204 func (ps *PublishService) ModifyIndAPFPubAPI(ctx echo.Context, apfId publishserviceapi.ApfId, serviceApiId publishserviceapi.ServiceApiId) error {
205         return ctx.NoContent(http.StatusNotImplemented)
206 }
207
208 func (ps *PublishService) PutApfIdServiceApisServiceApiId(ctx echo.Context, apfId publishserviceapi.ApfId, serviceApiId publishserviceapi.ServiceApiId) error {
209         return ctx.NoContent(http.StatusNotImplemented)
210 }
211
212 // This function wraps sending of an error in the Error format, and
213 // handling the failure to marshal that.
214 func sendCoreError(ctx echo.Context, code int, message string) error {
215         pd := common29122.ProblemDetails{
216                 Cause:  &message,
217                 Status: &code,
218         }
219         err := ctx.JSON(code, pd)
220         return err
221 }