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