Adding query APIs for AppIntents
[multicloud/k8s.git] / src / orchestrator / api / app_profilehandler.go
1 /*
2  * Copyright 2020 Intel Corporation, Inc
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package api
18
19 import (
20         "bytes"
21         "encoding/base64"
22         "encoding/json"
23         "io"
24         "io/ioutil"
25         "mime"
26         "mime/multipart"
27         "net/http"
28         "net/textproto"
29
30         "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/validation"
31         moduleLib "github.com/onap/multicloud-k8s/src/orchestrator/pkg/module"
32
33         "github.com/gorilla/mux"
34         pkgerrors "github.com/pkg/errors"
35 )
36
37 /* Used to store backend implementation objects
38 Also simplifies mocking for unit testing purposes
39 */
40 type appProfileHandler struct {
41         client moduleLib.AppProfileManager
42 }
43
44 // createAppProfileHandler handles the create operation
45 func (h appProfileHandler) createAppProfileHandler(w http.ResponseWriter, r *http.Request) {
46
47         vars := mux.Vars(r)
48         project := vars["project-name"]
49         compositeApp := vars["composite-app-name"]
50         compositeAppVersion := vars["composite-app-version"]
51         compositeProfile := vars["composite-profile-name"]
52
53         var ap moduleLib.AppProfile
54         var ac moduleLib.AppProfileContent
55
56         // Implemenation using multipart form
57         // Review and enable/remove at a later date
58         // Set Max size to 16mb here
59         err := r.ParseMultipartForm(16777216)
60         if err != nil {
61                 http.Error(w, err.Error(), http.StatusUnprocessableEntity)
62                 return
63         }
64
65         jsn := bytes.NewBuffer([]byte(r.FormValue("metadata")))
66         err = json.NewDecoder(jsn).Decode(&ap)
67         switch {
68         case err == io.EOF:
69                 http.Error(w, "Empty body", http.StatusBadRequest)
70                 return
71         case err != nil:
72                 http.Error(w, err.Error(), http.StatusUnprocessableEntity)
73                 return
74         }
75
76         //Read the file section and ignore the header
77         file, _, err := r.FormFile("file")
78         if err != nil {
79                 http.Error(w, "Unable to process file", http.StatusUnprocessableEntity)
80                 return
81         }
82
83         defer file.Close()
84
85         //Convert the file content to base64 for storage
86         content, err := ioutil.ReadAll(file)
87         if err != nil {
88                 http.Error(w, "Unable to read file", http.StatusUnprocessableEntity)
89                 return
90         }
91
92         err = validation.IsTarGz(bytes.NewBuffer(content))
93         if err != nil {
94                 http.Error(w, "Error in file format", http.StatusUnprocessableEntity)
95                 return
96         }
97
98         ac.Profile = base64.StdEncoding.EncodeToString(content)
99
100         // Name is required.
101         if ap.Metadata.Name == "" {
102                 http.Error(w, "Missing name in POST request", http.StatusBadRequest)
103                 return
104         }
105
106         ret, err := h.client.CreateAppProfile(project, compositeApp, compositeAppVersion, compositeProfile, ap, ac)
107         if err != nil {
108                 http.Error(w, err.Error(), http.StatusInternalServerError)
109                 return
110         }
111
112         w.WriteHeader(http.StatusCreated)
113         err = json.NewEncoder(w).Encode(ret)
114         if err != nil {
115                 http.Error(w, err.Error(), http.StatusInternalServerError)
116                 return
117         }
118 }
119
120 // getHandler handles the GET operations on AppProfile
121 func (h appProfileHandler) getAppProfileHandler(w http.ResponseWriter, r *http.Request) {
122         vars := mux.Vars(r)
123         project := vars["project-name"]
124         compositeApp := vars["composite-app-name"]
125         compositeAppVersion := vars["composite-app-version"]
126         compositeProfile := vars["composite-profile-name"]
127         name := vars["app-profile"]
128         appName := r.URL.Query().Get("app-name")
129
130         if len(name) != 0 && len(appName) != 0 {
131                 http.Error(w, pkgerrors.New("Invalid query").Error(), http.StatusInternalServerError)
132                 return
133         }
134
135         // handle the get all app profiles case - return a list of only the json parts
136         if len(name) == 0 && len(appName) == 0 {
137                 var retList []moduleLib.AppProfile
138
139                 ret, err := h.client.GetAppProfiles(project, compositeApp, compositeAppVersion, compositeProfile)
140                 if err != nil {
141                         http.Error(w, err.Error(), http.StatusInternalServerError)
142                         return
143                 }
144
145                 for _, ap := range ret {
146                         retList = append(retList, ap)
147                 }
148
149                 w.Header().Set("Content-Type", "application/json")
150                 w.WriteHeader(http.StatusOK)
151                 err = json.NewEncoder(w).Encode(retList)
152                 if err != nil {
153                         http.Error(w, err.Error(), http.StatusInternalServerError)
154                         return
155                 }
156                 return
157         }
158
159         accepted, _, err := mime.ParseMediaType(r.Header.Get("Accept"))
160         if err != nil {
161                 http.Error(w, err.Error(), http.StatusNotAcceptable)
162                 return
163         }
164
165         var retAppProfile moduleLib.AppProfile
166         var retAppProfileContent moduleLib.AppProfileContent
167
168         if len(appName) != 0 {
169                 retAppProfile, err = h.client.GetAppProfileByApp(project, compositeApp, compositeAppVersion, compositeProfile, appName)
170                 if err != nil {
171                         http.Error(w, err.Error(), http.StatusInternalServerError)
172                         return
173                 }
174
175                 retAppProfileContent, err = h.client.GetAppProfileContentByApp(project, compositeApp, compositeAppVersion, compositeProfile, appName)
176                 if err != nil {
177                         http.Error(w, err.Error(), http.StatusInternalServerError)
178                         return
179                 }
180         } else {
181                 retAppProfile, err = h.client.GetAppProfile(project, compositeApp, compositeAppVersion, compositeProfile, name)
182                 if err != nil {
183                         http.Error(w, err.Error(), http.StatusInternalServerError)
184                         return
185                 }
186
187                 retAppProfileContent, err = h.client.GetAppProfileContent(project, compositeApp, compositeAppVersion, compositeProfile, name)
188                 if err != nil {
189                         http.Error(w, err.Error(), http.StatusInternalServerError)
190                         return
191                 }
192         }
193
194         switch accepted {
195         case "multipart/form-data":
196                 mpw := multipart.NewWriter(w)
197                 w.Header().Set("Content-Type", mpw.FormDataContentType())
198                 w.WriteHeader(http.StatusOK)
199                 pw, err := mpw.CreatePart(textproto.MIMEHeader{"Content-Type": {"application/json"}, "Content-Disposition": {"form-data; name=metadata"}})
200                 if err != nil {
201                         http.Error(w, err.Error(), http.StatusInternalServerError)
202                         return
203                 }
204                 if err := json.NewEncoder(pw).Encode(retAppProfile); err != nil {
205                         http.Error(w, err.Error(), http.StatusInternalServerError)
206                         return
207                 }
208                 pw, err = mpw.CreatePart(textproto.MIMEHeader{"Content-Type": {"application/octet-stream"}, "Content-Disposition": {"form-data; name=file"}})
209                 if err != nil {
210                         http.Error(w, err.Error(), http.StatusInternalServerError)
211                         return
212                 }
213                 kcBytes, err := base64.StdEncoding.DecodeString(retAppProfileContent.Profile)
214                 if err != nil {
215                         http.Error(w, err.Error(), http.StatusInternalServerError)
216                         return
217                 }
218                 _, err = pw.Write(kcBytes)
219                 if err != nil {
220                         http.Error(w, err.Error(), http.StatusInternalServerError)
221                         return
222                 }
223         case "application/json":
224                 w.Header().Set("Content-Type", "application/json")
225                 w.WriteHeader(http.StatusOK)
226                 err = json.NewEncoder(w).Encode(retAppProfile)
227                 if err != nil {
228                         http.Error(w, err.Error(), http.StatusInternalServerError)
229                         return
230                 }
231         case "application/octet-stream":
232                 w.Header().Set("Content-Type", "application/octet-stream")
233                 w.WriteHeader(http.StatusOK)
234                 kcBytes, err := base64.StdEncoding.DecodeString(retAppProfileContent.Profile)
235                 if err != nil {
236                         http.Error(w, err.Error(), http.StatusInternalServerError)
237                         return
238                 }
239                 _, err = w.Write(kcBytes)
240                 if err != nil {
241                         http.Error(w, err.Error(), http.StatusInternalServerError)
242                         return
243                 }
244         default:
245                 http.Error(w, "set Accept: multipart/form-data, application/json or application/octet-stream", http.StatusMultipleChoices)
246                 return
247         }
248 }
249
250 // deleteHandler handles the delete operations on AppProfile
251 func (h appProfileHandler) deleteAppProfileHandler(w http.ResponseWriter, r *http.Request) {
252         vars := mux.Vars(r)
253         project := vars["project-name"]
254         compositeApp := vars["composite-app-name"]
255         compositeAppVersion := vars["composite-app-version"]
256         compositeProfile := vars["composite-profile-name"]
257         name := vars["app-profile"]
258
259         err := h.client.DeleteAppProfile(project, compositeApp, compositeAppVersion, compositeProfile, name)
260         if err != nil {
261                 http.Error(w, err.Error(), http.StatusInternalServerError)
262                 return
263         }
264
265         w.WriteHeader(http.StatusNoContent)
266 }