@@ -2,14 +2,14 @@ package controllers
22
33import (
44 "encoding/json"
5- "errors"
6- "fmt"
75 "net/http"
86 "os"
7+ "strconv"
98
109 "github.com/Parallels/prl-devops-service/basecontext"
1110 "github.com/Parallels/prl-devops-service/config"
1211 "github.com/Parallels/prl-devops-service/constants"
12+ prlerrors "github.com/Parallels/prl-devops-service/errors"
1313 "github.com/Parallels/prl-devops-service/logs"
1414 "github.com/Parallels/prl-devops-service/mappers"
1515 "github.com/Parallels/prl-devops-service/models"
@@ -104,28 +104,27 @@ func registerConfigHandlers(ctx basecontext.ApiContext, version string) {
104104// @Tags Config
105105// @Produce json
106106// @Success 200 {object} models.ParallelsDesktopLicense
107- // @Failure 400 {object} models.ApiErrorResponse
107+ // @Failure 400 {object} models.ApiErrorDiagnosticsResponse
108108// @Failure 401 {object} models.OAuthErrorResponse
109109// @Security ApiKeyAuth
110110// @Security BearerAuth
111- // @Router /v1/parallels_desktop/key [get]
111+ // @Router /v1/config/parallels-desktop/license [get]
112112func GetParallelsDesktopLicenseHandler () restapi.ControllerHandler {
113113 return func (w http.ResponseWriter , r * http.Request ) {
114114 defer r .Body .Close ()
115115 ctx := GetBaseContext (r )
116116 defer Recover (ctx , r , w )
117+ getPDLicenseDiag := prlerrors .NewDiagnostics ("/config/parallels-desktop/license" )
117118 provider := serviceprovider .Get ()
118119 if provider .ParallelsDesktopService == nil || ! provider .ParallelsDesktopService .Installed () {
119- ReturnApiError (ctx , w , models.ApiErrorResponse {
120- Message : "Parallels Desktop is not installed" ,
121- Code : http .StatusNotFound ,
122- })
120+ getPDLicenseDiag .AddError ("404" , "Parallels Desktop is not installed" , "GetParallelsDesktopLicense" )
121+ ReturnApiErrorWithDiagnostics (ctx , w , models .NewDiagnosticsWithCode (getPDLicenseDiag , http .StatusNotFound ))
123122 return
124123 }
125124
126- license , err := provider .ParallelsDesktopService .GetLicense ()
127- if err != nil {
128- ReturnApiError (ctx , w , models .NewFromError ( err ))
125+ license := provider .ParallelsDesktopService .GetLicense (getPDLicenseDiag )
126+ if getPDLicenseDiag . HasErrors () {
127+ ReturnApiErrorWithDiagnostics (ctx , w , models .NewDiagnosticsWithCode ( getPDLicenseDiag , http . StatusInternalServerError ))
129128 return
130129 }
131130
@@ -141,7 +140,7 @@ func GetParallelsDesktopLicenseHandler() restapi.ControllerHandler {
141140// @Produce json
142141// @Param installToolsRequest body models.InstallToolsRequest true "Install Tools Request"
143142// @Success 200 {object} models.InstallToolsResponse
144- // @Failure 400 {object} models.ApiErrorResponse
143+ // @Failure 400 {object} models.ApiErrorDiagnosticsResponse
145144// @Failure 401 {object} models.OAuthErrorResponse
146145// @Security ApiKeyAuth
147146// @Security BearerAuth
@@ -151,19 +150,16 @@ func Install3rdPartyToolsHandler() restapi.ControllerHandler {
151150 defer r .Body .Close ()
152151 ctx := GetBaseContext (r )
153152 defer Recover (ctx , r , w )
153+ installToolsDiag := prlerrors .NewDiagnostics ("/config/tools/install" )
154154 var request models.InstallToolsRequest
155155 if err := http_helper .MapRequestBody (r , & request ); err != nil {
156- ReturnApiError (ctx , w , models.ApiErrorResponse {
157- Message : "Invalid request body: " + err .Error (),
158- Code : http .StatusBadRequest ,
159- })
156+ installToolsDiag .AddError (strconv .Itoa (http .StatusBadRequest ), "Invalid request body: " + err .Error (), "MapRequestBody" )
157+ ReturnApiErrorWithDiagnostics (ctx , w , models .NewDiagnosticsWithCode (installToolsDiag , http .StatusBadRequest ))
160158 return
161159 }
162- if err := request .Validate (); err != nil {
163- ReturnApiError (ctx , w , models.ApiErrorResponse {
164- Message : "Invalid request body: " + err .Error (),
165- Code : http .StatusBadRequest ,
166- })
160+ request .Validate (installToolsDiag )
161+ if installToolsDiag .HasErrors () {
162+ ReturnApiErrorWithDiagnostics (ctx , w , models .NewDiagnosticsWithCode (installToolsDiag , http .StatusBadRequest ))
167163 return
168164 }
169165
@@ -205,7 +201,7 @@ func Install3rdPartyToolsHandler() restapi.ControllerHandler {
205201// @Produce json
206202// @Param uninstallToolsRequest body models.UninstallToolsRequest true "Uninstall Tools Request"
207203// @Success 200 {object} models.InstallToolsResponse
208- // @Failure 400 {object} models.ApiErrorResponse
204+ // @Failure 400 {object} models.ApiErrorDiagnosticsResponse
209205// @Failure 401 {object} models.OAuthErrorResponse
210206// @Security ApiKeyAuth
211207// @Security BearerAuth
@@ -215,19 +211,16 @@ func Uninstall3rdPartyToolsHandler() restapi.ControllerHandler {
215211 defer r .Body .Close ()
216212 ctx := GetBaseContext (r )
217213 defer Recover (ctx , r , w )
214+ uninstallToolsDiag := prlerrors .NewDiagnostics ("/config/tools/uninstall" )
218215 var request models.UninstallToolsRequest
219216 if err := http_helper .MapRequestBody (r , & request ); err != nil {
220- ReturnApiError (ctx , w , models.ApiErrorResponse {
221- Message : "Invalid request body: " + err .Error (),
222- Code : http .StatusBadRequest ,
223- })
217+ uninstallToolsDiag .AddError (strconv .Itoa (http .StatusBadRequest ), "Invalid request body: " + err .Error (), "MapRequestBody" )
218+ ReturnApiErrorWithDiagnostics (ctx , w , models .NewDiagnosticsWithCode (uninstallToolsDiag , http .StatusBadRequest ))
224219 return
225220 }
226- if err := request .Validate (); err != nil {
227- ReturnApiError (ctx , w , models.ApiErrorResponse {
228- Message : "Invalid request body: " + err .Error (),
229- Code : http .StatusBadRequest ,
230- })
221+ request .Validate (uninstallToolsDiag )
222+ if uninstallToolsDiag .HasErrors () {
223+ ReturnApiErrorWithDiagnostics (ctx , w , models .NewDiagnosticsWithCode (uninstallToolsDiag , http .StatusBadRequest ))
231224 return
232225 }
233226
@@ -265,7 +258,7 @@ func Uninstall3rdPartyToolsHandler() restapi.ControllerHandler {
265258// @Tags Config
266259// @Produce json
267260// @Success 202
268- // @Failure 400 {object} models.ApiErrorResponse
261+ // @Failure 400 {object} models.ApiErrorDiagnosticsResponse
269262// @Failure 401 {object} models.OAuthErrorResponse
270263// @Security ApiKeyAuth
271264// @Security BearerAuth
@@ -286,7 +279,7 @@ func RestartApiHandler() restapi.ControllerHandler {
286279// @Tags Config
287280// @Produce json
288281// @Success 200 {object} models.SystemUsageResponse
289- // @Failure 400 {object} models.ApiErrorResponse
282+ // @Failure 400 {object} models.ApiErrorDiagnosticsResponse
290283// @Failure 401 {object} models.OAuthErrorResponse
291284// @Security ApiKeyAuth
292285// @Security BearerAuth
@@ -297,14 +290,14 @@ func GetHardwareInfo() restapi.ControllerHandler {
297290 ctx := GetBaseContext (r )
298291 cfg := config .Get ()
299292 defer Recover (ctx , r , w )
293+ getHardwareInfoDiag := prlerrors .NewDiagnostics ("/config/hardware" )
300294 provider := serviceprovider .Get ()
301295 os := system .Get ().GetOperatingSystem ()
302296 var hardwareInfo * models.SystemUsageResponse
303- var err error
304297 if os == "macos" {
305- hardwareInfo , err = provider .ParallelsDesktopService .GetHardwareUsage (ctx )
298+ hardwareInfo = provider .ParallelsDesktopService .GetHardwareUsage (ctx , getHardwareInfoDiag )
306299 } else {
307- hardwareInfo , err = provider .System .GetHardwareUsage (ctx )
300+ hardwareInfo = provider .System .GetHardwareUsage (ctx , getHardwareInfoDiag )
308301 }
309302
310303 hardwareInfo .EnabledModules = cfg .GetEnabledModules ()
@@ -334,7 +327,7 @@ func GetHardwareInfo() restapi.ControllerHandler {
334327 }
335328
336329 var freeDiskSpace int64
337- if ds , err := diskspace .Get (ctx ).GetCacheDiskSpace (ctx ); err == nil {
330+ if ds := diskspace .Get (ctx ).GetCacheDiskSpace (ctx , getHardwareInfoDiag ); ! getHardwareInfoDiag . HasErrors () {
338331 freeDiskSpace = ds
339332 } else if hwInfo , err := provider .System .GetHardwareInfo (ctx ); err == nil {
340333 freeDiskSpace = int64 (hwInfo .FreeDiskSize )
@@ -353,8 +346,15 @@ func GetHardwareInfo() restapi.ControllerHandler {
353346 }
354347 }
355348
356- if err != nil || hardwareInfo == nil {
357- ReturnApiError (ctx , w , models .NewFromError (err ))
349+ // 1. First, safely check if the error is what failed
350+ if getHardwareInfoDiag .HasErrors () {
351+ ReturnApiErrorWithDiagnostics (ctx , w , models .NewDiagnosticsWithCode (getHardwareInfoDiag , http .StatusInternalServerError ))
352+ return
353+ }
354+ // 2. ONLY if there was no error do we check if the data is missing!
355+ if hardwareInfo == nil {
356+ getHardwareInfoDiag .AddError ("500" , "Hardware info not found" , "GetHardwareUsage" )
357+ ReturnApiErrorWithDiagnostics (ctx , w , models .NewDiagnosticsWithCode (getHardwareInfoDiag , http .StatusInternalServerError ))
358358 return
359359 }
360360
@@ -373,6 +373,8 @@ func GetHardwareInfo() restapi.ControllerHandler {
373373func GetSystemHealth () restapi.ControllerHandler {
374374 return func (w http.ResponseWriter , r * http.Request ) {
375375 defer r .Body .Close ()
376+ ctx := GetBaseContext (r )
377+ defer Recover (ctx , r , w )
376378 provider := serviceprovider .Get ()
377379 result := models.ApiHealthCheck {}
378380
@@ -445,7 +447,7 @@ func GetSystemHealth() restapi.ControllerHandler {
445447// @Tags Config
446448// @Produce plain
447449// @Success 200
448- // @Failure 400 {object} models.ApiErrorResponse
450+ // @Failure 400 {object} models.ApiErrorDiagnosticsResponse
449451// @Failure 401 {object} models.OAuthErrorResponse
450452// @Security ApiKeyAuth
451453// @Security BearerAuth
@@ -455,34 +457,28 @@ func GetSystemLogs() restapi.ControllerHandler {
455457 defer r .Body .Close ()
456458 ctx := GetBaseContext (r )
457459 defer Recover (ctx , r , w )
458-
460+ getSystemLogsDiag := prlerrors . NewDiagnostics ( "/logs" )
459461 cfg := config .Get ()
460462
461463 // Checking if we have the logs to file enabled so we can read the logs
462464 if ! cfg .GetBoolKey (constants .LOG_TO_FILE_ENV_VAR ) && ! cfg .GetBoolKey (constants .PRL_DEVOPS_LOG_TO_FILE_ENV_VAR ) {
463- err := errors .New ("logs to file is not enabled, we cannot read the logs" )
464- ReturnApiError (ctx , w , models.ApiErrorResponse {
465- Message : "Failed to read log file: " + err .Error (),
466- Code : http .StatusBadRequest ,
467- })
465+ getSystemLogsDiag .AddError (strconv .Itoa (http .StatusBadRequest ), "logs to file is not enabled, we cannot read the logs" , "GetBoolKey" )
466+ ReturnApiErrorWithDiagnostics (ctx , w , models .NewDiagnosticsWithCode (getSystemLogsDiag , http .StatusBadRequest ))
468467 return
469468 }
470469
471470 logFile := logs .GetLogFilePath (ctx )
472471 if logFile == "" {
473- ReturnApiError (ctx , w , models.ApiErrorResponse {
474- Message : "Failed to read log file: log file path is empty" ,
475- Code : http .StatusBadRequest ,
476- })
472+ getSystemLogsDiag .AddError (strconv .Itoa (http .StatusBadRequest ), "Failed to read log file: log file path is empty" , "GetLogFilePath" )
473+ ReturnApiErrorWithDiagnostics (ctx , w , models .NewDiagnosticsWithCode (getSystemLogsDiag , http .StatusBadRequest ))
477474 return
478475 }
479476
480477 content , err := os .ReadFile (logFile )
481478 if err != nil {
482- ReturnApiError (ctx , w , models.ApiErrorResponse {
483- Message : fmt .Sprintf ("Failed to read log file %s: %v" , logFile , err .Error ()),
484- Code : http .StatusBadRequest ,
485- })
479+ rsp := models .NewFromError (err )
480+ getSystemLogsDiag .AddError (strconv .Itoa (rsp .Code ), rsp .Message , "Failed to read log file" )
481+ ReturnApiErrorWithDiagnostics (ctx , w , models .NewDiagnosticsWithCode (getSystemLogsDiag , rsp .Code ))
486482 return
487483 }
488484
@@ -497,7 +493,7 @@ func GetSystemLogs() restapi.ControllerHandler {
497493// @Tags Config
498494// @Produce json
499495// @Success 101 "Switching Protocols to websocket"
500- // @Failure 400 {object} models.ApiErrorResponse
496+ // @Failure 400 {object} models.ApiErrorDiagnosticsResponse
501497// @Failure 401 {object} models.OAuthErrorResponse
502498// @Security ApiKeyAuth
503499// @Security BearerAuth
@@ -569,7 +565,7 @@ func StreamSystemLogs() restapi.ControllerHandler {
569565// @Produce json
570566// @Param createRequest body models.DiskSpaceAvailableRequest false "Disk Space Available Request"
571567// @Success 200 {object} models.DiskSpaceAvailable
572- // @Failure 400 {object} models.ApiErrorResponse
568+ // @Failure 400 {object} models.ApiErrorDiagnosticsResponse
573569// @Failure 401 {object} models.OAuthErrorResponse
574570// @Security ApiKeyAuth
575571// @Security BearerAuth
@@ -579,19 +575,17 @@ func GetParallelsDiskSpace() restapi.ControllerHandler {
579575 defer r .Body .Close ()
580576 ctx := GetBaseContext (r )
581577 defer Recover (ctx , r , w )
582-
578+ getDiskSpaceAvailableDiag := prlerrors . NewDiagnostics ( "/config/diskspace" )
583579 var request models.DiskSpaceAvailableRequest
584580 if err := http_helper .MapRequestBody (r , & request ); err != nil {
585- ReturnApiError (ctx , w , models.ApiErrorResponse {
586- Message : "Invalid request body: " + err .Error (),
587- Code : http .StatusBadRequest ,
588- })
581+ getDiskSpaceAvailableDiag .AddError (strconv .Itoa (http .StatusBadRequest ), "Invalid request body: " + err .Error (), "MapRequestBody" )
582+ ReturnApiErrorWithDiagnostics (ctx , w , models .NewDiagnosticsWithCode (getDiskSpaceAvailableDiag , http .StatusBadRequest ))
589583 return
590584 }
591585
592- response , err := diskspace .Get (ctx ).GetDiskSpaceAvailable (ctx , request .UserName , request .FolderPath )
593- if err != nil {
594- ReturnApiError (ctx , w , models .NewFromError ( err ))
586+ response := diskspace .Get (ctx ).GetDiskSpaceAvailable (ctx , request .UserName , request .FolderPath , getDiskSpaceAvailableDiag )
587+ if getDiskSpaceAvailableDiag . HasErrors () {
588+ ReturnApiErrorWithDiagnostics (ctx , w , models .NewDiagnosticsWithCode ( getDiskSpaceAvailableDiag , http . StatusInternalServerError ))
595589 return
596590 }
597591
0 commit comments