Update invokermanagement
[nonrtric/plt/sme.git] / capifcore / internal / providermanagement / providermanagement.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 providermanagement
22
23 import (
24         "fmt"
25         "net/http"
26         "path"
27         "strings"
28         "sync"
29
30         "github.com/labstack/echo/v4"
31
32         "oransc.org/nonrtric/capifcore/internal/common29122"
33         provapi "oransc.org/nonrtric/capifcore/internal/providermanagementapi"
34
35         log "github.com/sirupsen/logrus"
36 )
37
38 //go:generate mockery --name ServiceRegister
39 type ServiceRegister interface {
40         IsFunctionRegistered(functionId string) bool
41         GetAefsForPublisher(apfId string) []string
42 }
43
44 type ProviderManager struct {
45         onboardedProviders map[string]provapi.APIProviderEnrolmentDetails
46         lock               sync.Mutex
47 }
48
49 func NewProviderManager() *ProviderManager {
50         return &ProviderManager{
51                 onboardedProviders: make(map[string]provapi.APIProviderEnrolmentDetails),
52         }
53 }
54
55 func (pm *ProviderManager) IsFunctionRegistered(functionId string) bool {
56         registered := false
57 out:
58         for _, provider := range pm.onboardedProviders {
59                 for _, registeredFunc := range *provider.ApiProvFuncs {
60                         if *registeredFunc.ApiProvFuncId == functionId {
61                                 registered = true
62                                 break out
63                         }
64                 }
65         }
66
67         return registered
68 }
69
70 func (pm *ProviderManager) GetAefsForPublisher(apfId string) []string {
71         for _, provider := range pm.onboardedProviders {
72                 for _, registeredFunc := range *provider.ApiProvFuncs {
73                         if *registeredFunc.ApiProvFuncId == apfId && registeredFunc.ApiProvFuncRole == provapi.ApiProviderFuncRoleAPF {
74                                 return getExposedFuncs(provider.ApiProvFuncs)
75                         }
76                 }
77         }
78         return nil
79 }
80
81 func getExposedFuncs(providerFuncs *[]provapi.APIProviderFunctionDetails) []string {
82         exposedFuncs := []string{}
83         for _, registeredFunc := range *providerFuncs {
84                 if registeredFunc.ApiProvFuncRole == provapi.ApiProviderFuncRoleAEF {
85                         exposedFuncs = append(exposedFuncs, *registeredFunc.ApiProvFuncId)
86                 }
87         }
88         return exposedFuncs
89 }
90
91 func (pm *ProviderManager) PostRegistrations(ctx echo.Context) error {
92         var newProvider provapi.APIProviderEnrolmentDetails
93         err := ctx.Bind(&newProvider)
94         if err != nil {
95                 return sendCoreError(ctx, http.StatusBadRequest, "Invalid format for provider")
96         }
97
98         if newProvider.ApiProvDomInfo == nil || *newProvider.ApiProvDomInfo == "" {
99                 return sendCoreError(ctx, http.StatusBadRequest, "Provider missing required ApiProvDomInfo")
100         }
101
102         pm.lock.Lock()
103         defer pm.lock.Unlock()
104
105         newProvider.ApiProvDomId = pm.getDomainId(newProvider.ApiProvDomInfo)
106
107         pm.registerFunctions(newProvider.ApiProvFuncs)
108         pm.onboardedProviders[*newProvider.ApiProvDomId] = newProvider
109
110         uri := ctx.Request().Host + ctx.Request().URL.String()
111         ctx.Response().Header().Set(echo.HeaderLocation, ctx.Scheme()+`://`+path.Join(uri, *newProvider.ApiProvDomId))
112         err = ctx.JSON(http.StatusCreated, newProvider)
113         if err != nil {
114                 // Something really bad happened, tell Echo that our handler failed
115                 return err
116         }
117
118         return nil
119 }
120
121 func (pm *ProviderManager) DeleteRegistrationsRegistrationId(ctx echo.Context, registrationId string) error {
122         pm.lock.Lock()
123         defer pm.lock.Unlock()
124
125         log.Debug(pm.onboardedProviders)
126         if _, ok := pm.onboardedProviders[registrationId]; ok {
127                 log.Debug("Deleting provider", registrationId)
128                 delete(pm.onboardedProviders, registrationId)
129         }
130
131         return ctx.NoContent(http.StatusNoContent)
132 }
133
134 func (pm *ProviderManager) PutRegistrationsRegistrationId(ctx echo.Context, registrationId string) error {
135         pm.lock.Lock()
136         defer pm.lock.Unlock()
137
138         registeredProvider, shouldReturn, returnValue := pm.checkIfProviderIsRegistered(registrationId, ctx)
139         if shouldReturn {
140                 return returnValue
141         }
142
143         updatedProvider, shouldReturn1, returnValue1 := getProviderFromRequest(ctx)
144         if shouldReturn1 {
145                 return returnValue1
146         }
147
148         if updatedProvider.ApiProvDomInfo != nil {
149                 registeredProvider.ApiProvDomInfo = updatedProvider.ApiProvDomInfo
150         }
151
152         shouldReturn, returnValue = pm.updateFunctions(updatedProvider, registeredProvider, ctx)
153         if shouldReturn {
154                 return returnValue
155         }
156
157         err := ctx.JSON(http.StatusOK, pm.onboardedProviders[registrationId])
158         if err != nil {
159                 // Something really bad happened, tell Echo that our handler failed
160                 return err
161         }
162
163         return nil
164 }
165
166 func (pm *ProviderManager) checkIfProviderIsRegistered(registrationId string, ctx echo.Context) (provapi.APIProviderEnrolmentDetails, bool, error) {
167         registeredProvider, ok := pm.onboardedProviders[registrationId]
168         if !ok {
169                 return provapi.APIProviderEnrolmentDetails{}, true, sendCoreError(ctx, http.StatusBadRequest, "Provider must be onboarded before updating it")
170         }
171         return registeredProvider, false, nil
172 }
173
174 func getProviderFromRequest(ctx echo.Context) (provapi.APIProviderEnrolmentDetails, bool, error) {
175         var updatedProvider provapi.APIProviderEnrolmentDetails
176         err := ctx.Bind(&updatedProvider)
177         if err != nil {
178                 return provapi.APIProviderEnrolmentDetails{}, true, sendCoreError(ctx, http.StatusBadRequest, "Invalid format for provider")
179         }
180         return updatedProvider, false, nil
181 }
182
183 func (pm *ProviderManager) updateFunctions(updatedProvider provapi.APIProviderEnrolmentDetails, registeredProvider provapi.APIProviderEnrolmentDetails, ctx echo.Context) (bool, error) {
184         for _, function := range *updatedProvider.ApiProvFuncs {
185                 if function.ApiProvFuncId == nil {
186                         pm.addFunction(function, registeredProvider)
187                 } else {
188                         shouldReturn, returnValue := pm.updateFunction(function, registeredProvider, ctx)
189                         if shouldReturn {
190                                 return true, returnValue
191                         }
192                 }
193         }
194         return false, nil
195 }
196
197 func (pm *ProviderManager) addFunction(function provapi.APIProviderFunctionDetails, registeredProvider provapi.APIProviderEnrolmentDetails) {
198         function.ApiProvFuncId = pm.getFuncId(function.ApiProvFuncRole, function.ApiProvFuncInfo)
199         registeredFuncs := *registeredProvider.ApiProvFuncs
200         newFuncs := append(registeredFuncs, function)
201         registeredProvider.ApiProvFuncs = &newFuncs
202         pm.onboardedProviders[*registeredProvider.ApiProvDomId] = registeredProvider
203 }
204
205 func (*ProviderManager) updateFunction(function provapi.APIProviderFunctionDetails, registeredProvider provapi.APIProviderEnrolmentDetails, ctx echo.Context) (bool, error) {
206         pos, registeredFunction, err := getApiFunc(*function.ApiProvFuncId, registeredProvider.ApiProvFuncs)
207         if err != nil {
208                 return true, sendCoreError(ctx, http.StatusBadRequest, "Unable to update provider due to: "+err.Error())
209         }
210         if function.ApiProvFuncInfo != nil {
211                 registeredFunction.ApiProvFuncInfo = function.ApiProvFuncInfo
212                 (*registeredProvider.ApiProvFuncs)[pos] = registeredFunction
213         }
214         return false, nil
215 }
216
217 func getApiFunc(funcId string, apiFunctions *[]provapi.APIProviderFunctionDetails) (int, provapi.APIProviderFunctionDetails, error) {
218         for pos, function := range *apiFunctions {
219                 if *function.ApiProvFuncId == funcId {
220                         return pos, function, nil
221                 }
222         }
223         return 0, provapi.APIProviderFunctionDetails{}, fmt.Errorf("function with ID %s is not registered for the provider", funcId)
224 }
225
226 func (pm *ProviderManager) ModifyIndApiProviderEnrolment(ctx echo.Context, registrationId string) error {
227         return ctx.NoContent(http.StatusNotImplemented)
228 }
229
230 func (pm *ProviderManager) registerFunctions(provFuncs *[]provapi.APIProviderFunctionDetails) {
231         if provFuncs == nil {
232                 return
233         }
234         for i, provFunc := range *provFuncs {
235                 (*provFuncs)[i].ApiProvFuncId = pm.getFuncId(provFunc.ApiProvFuncRole, provFunc.ApiProvFuncInfo)
236         }
237 }
238
239 func (pm *ProviderManager) getDomainId(domainInfo *string) *string {
240         idAsString := "domain_id_" + strings.ReplaceAll(*domainInfo, " ", "_")
241         return &idAsString
242 }
243
244 func (pm *ProviderManager) getFuncId(role provapi.ApiProviderFuncRole, funcInfo *string) *string {
245         var idPrefix string
246         switch role {
247         case provapi.ApiProviderFuncRoleAPF:
248                 idPrefix = "APF_id_"
249         case provapi.ApiProviderFuncRoleAMF:
250                 idPrefix = "AMF_id_"
251         case provapi.ApiProviderFuncRoleAEF:
252                 idPrefix = "AEF_id_"
253         default:
254                 idPrefix = "function_id_"
255         }
256         idAsString := idPrefix + strings.ReplaceAll(*funcInfo, " ", "_")
257         return &idAsString
258 }
259
260 // This function wraps sending of an error in the Error format, and
261 // handling the failure to marshal that.
262 func sendCoreError(ctx echo.Context, code int, message string) error {
263         pd := common29122.ProblemDetails{
264                 Cause:  &message,
265                 Status: &code,
266         }
267         err := ctx.JSON(code, pd)
268         return err
269 }