NONRTRIC-946: Fix Capifcore intersection panic
[nonrtric/plt/sme.git] / capifcore / internal / providermanagement / providermanagement.go
1 // -
2 //   ========================LICENSE_START=================================
3 //   O-RAN-SC
4 //   %%
5 //   Copyright (C) 2022-2023: Nordix Foundation
6 //   Copyright (C) 2024: OpenInfra Foundation Europe
7 //   %%
8 //   Licensed under the Apache License, Version 2.0 (the "License");
9 //   you may not use this file except in compliance with the License.
10 //   You may obtain a copy of the License at
11 //
12 //        http://www.apache.org/licenses/LICENSE-2.0
13 //
14 //   Unless required by applicable law or agreed to in writing, software
15 //   distributed under the License is distributed on an "AS IS" BASIS,
16 //   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 //   See the License for the specific language governing permissions and
18 //   limitations under the License.
19 //   ========================LICENSE_END===================================
20 //
21
22 package providermanagement
23
24 import (
25         "fmt"
26         "net/http"
27         "path"
28         "sync"
29
30         echo "github.com/labstack/echo/v4"
31         log "github.com/sirupsen/logrus"
32
33         "oransc.org/nonrtric/capifcore/internal/common29122"
34         provapi "oransc.org/nonrtric/capifcore/internal/providermanagementapi"
35 )
36
37 //go:generate mockery --name ServiceRegister
38 type ServiceRegister interface {
39         IsFunctionRegistered(functionId string) bool
40         GetAefsForPublisher(apfId string) []string
41         IsPublishingFunctionRegistered(apiProvFuncId string) bool
42 }
43
44 type ProviderManager struct {
45         registeredProviders map[string]provapi.APIProviderEnrolmentDetails
46         lock                sync.Mutex
47 }
48
49 func NewProviderManager() *ProviderManager {
50         return &ProviderManager{
51                 registeredProviders: make(map[string]provapi.APIProviderEnrolmentDetails),
52         }
53 }
54
55 func (pm *ProviderManager) IsFunctionRegistered(functionId string) bool {
56         for _, provider := range pm.registeredProviders {
57                 if provider.IsFunctionRegistered(functionId) {
58                         return true
59                 }
60         }
61         return false
62 }
63
64 func (pm *ProviderManager) GetAefsForPublisher(apfId string) []string {
65         for _, provider := range pm.registeredProviders {
66                 if aefs := provider.GetExposingFunctionIdsForPublisher(apfId); aefs != nil {
67                         return aefs
68                 }
69         }
70         return nil
71 }
72
73 func (pm *ProviderManager) IsPublishingFunctionRegistered(apiProvFuncId string) bool {
74         for _, provider := range pm.registeredProviders {
75                 if provider.IsPublishingFunctionRegistered(apiProvFuncId) {
76                         return true
77                 }
78         }
79         return false
80 }
81
82 func (pm *ProviderManager) PostRegistrations(ctx echo.Context) error {
83         var newProvider provapi.APIProviderEnrolmentDetails
84         errMsg := "Unable to register provider due to %s"
85         if err := ctx.Bind(&newProvider); err != nil {
86                 return sendCoreError(ctx, http.StatusBadRequest, fmt.Sprintf(errMsg, "invalid format for provider"))
87         }
88
89         if err := pm.isProviderRegistered(newProvider); err != nil {
90                 return sendCoreError(ctx, http.StatusForbidden, fmt.Sprintf(errMsg, err))
91         }
92
93         if err := newProvider.Validate(); err != nil {
94                 return sendCoreError(ctx, http.StatusBadRequest, fmt.Sprintf(errMsg, err))
95         }
96
97         pm.prepareNewProvider(&newProvider)
98
99         uri := ctx.Request().Host + ctx.Request().URL.String()
100         ctx.Response().Header().Set(echo.HeaderLocation, ctx.Scheme()+`://`+path.Join(uri, *newProvider.ApiProvDomId))
101         if err := ctx.JSON(http.StatusCreated, newProvider); err != nil {
102                 // Something really bad happened, tell Echo that our handler failed
103                 return err
104         }
105         return nil
106 }
107
108 func (pm *ProviderManager) isProviderRegistered(newProvider provapi.APIProviderEnrolmentDetails) error {
109         for _, prov := range pm.registeredProviders {
110                 if err := prov.ValidateAlreadyRegistered(newProvider); err != nil {
111                         return err
112                 }
113         }
114         return nil
115 }
116
117 func (pm *ProviderManager) prepareNewProvider(newProvider *provapi.APIProviderEnrolmentDetails) {
118         pm.lock.Lock()
119         defer pm.lock.Unlock()
120
121         newProvider.PrepareNewProvider()
122         pm.registeredProviders[*newProvider.ApiProvDomId] = *newProvider
123 }
124
125 func (pm *ProviderManager) DeleteRegistrationsRegistrationId(ctx echo.Context, registrationId string) error {
126         log.Debug(pm.registeredProviders)
127         if _, ok := pm.registeredProviders[registrationId]; ok {
128                 pm.deleteProvider(registrationId)
129         }
130         return ctx.NoContent(http.StatusNoContent)
131 }
132
133 func (pm *ProviderManager) deleteProvider(registrationId string) {
134         log.Debug("Deleting provider", registrationId)
135         pm.lock.Lock()
136         defer pm.lock.Unlock()
137         delete(pm.registeredProviders, registrationId)
138 }
139
140 func (pm *ProviderManager) PutRegistrationsRegistrationId(ctx echo.Context, registrationId string) error {
141         errMsg := "Unable to update provider due to %s."
142         registeredProvider, err := pm.checkIfProviderIsRegistered(registrationId)
143         if err != nil {
144                 return sendCoreError(ctx, http.StatusBadRequest, fmt.Sprintf(errMsg, err))
145         }
146
147         updatedProvider, err := getProviderFromRequest(ctx)
148         if err != nil {
149                 return sendCoreError(ctx, http.StatusBadRequest, fmt.Sprintf(errMsg, err))
150         }
151
152         if updatedProvider.Validate() != nil {
153                 return sendCoreError(ctx, http.StatusBadRequest, fmt.Sprintf(errMsg, err))
154         }
155
156         // Additional validation for PUT
157         if (updatedProvider.ApiProvDomId == nil) || (*updatedProvider.ApiProvDomId != registrationId) {
158                 errDetail := "APIProviderEnrolmentDetails ApiProvDomId doesn't match path parameter"
159                 return sendCoreError(ctx, http.StatusBadRequest, fmt.Sprintf(errMsg, errDetail))
160         }
161
162         if err = pm.updateProvider(updatedProvider, registeredProvider); err != nil {
163                 return sendCoreError(ctx, http.StatusBadRequest, fmt.Sprintf(errMsg, err))
164         }
165
166         if err = ctx.JSON(http.StatusOK, updatedProvider); err != nil {
167                 // Something really bad happened, tell Echo that our handler failed
168                 return err
169         }
170         return nil
171 }
172
173 func (pm *ProviderManager) ModifyIndApiProviderEnrolment(ctx echo.Context, registrationId string) error {
174         return ctx.NoContent(http.StatusNotImplemented)
175 }
176
177 func (pm *ProviderManager) checkIfProviderIsRegistered(registrationId string) (*provapi.APIProviderEnrolmentDetails, error) {
178         registeredProvider, ok := pm.registeredProviders[registrationId]
179         if !ok {
180                 return nil, fmt.Errorf("provider not onboarded")
181         }
182         return &registeredProvider, nil
183 }
184
185 func getProviderFromRequest(ctx echo.Context) (provapi.APIProviderEnrolmentDetails, error) {
186         var updatedProvider provapi.APIProviderEnrolmentDetails
187         err := ctx.Bind(&updatedProvider)
188         if err != nil {
189                 return provapi.APIProviderEnrolmentDetails{}, fmt.Errorf("invalid format for provider")
190         }
191         return updatedProvider, nil
192 }
193
194 func (pm *ProviderManager) updateProvider(updatedProvider provapi.APIProviderEnrolmentDetails, registeredProvider *provapi.APIProviderEnrolmentDetails) error {
195         pm.lock.Lock()
196         defer pm.lock.Unlock()
197
198         if err := updatedProvider.UpdateFuncs(*registeredProvider); err == nil {
199                 pm.registeredProviders[*updatedProvider.ApiProvDomId] = updatedProvider
200                 return nil
201         } else {
202                 return err
203         }
204 }
205
206 // This function wraps sending of an error in the Error format, and
207 // handling the failure to marshal that.
208 func sendCoreError(ctx echo.Context, code int, message string) error {
209         pd := common29122.ProblemDetails{
210                 Cause:  &message,
211                 Status: &code,
212         }
213         err := ctx.JSON(code, pd)
214         return err
215 }