From 61867b1b9c2d9ea37e7b3b4b97262d5b5bed69dd Mon Sep 17 00:00:00 2001
From: Calvin Lee <pounce@fb.com>
Date: Mon, 25 Jul 2022 11:27:32 -0700
Subject: [PATCH] Use `Content` for ParseError and log separately

---
 http/mdm/mdm.go | 21 +++++++++++++++++++--
 mdm/checkin.go  |  2 +-
 mdm/command.go  |  4 ++--
 mdm/mdm.go      |  7 +++++--
 4 files changed, 27 insertions(+), 7 deletions(-)

diff --git a/http/mdm/mdm.go b/http/mdm/mdm.go
index 0325280..2d6633f 100644
--- a/http/mdm/mdm.go
+++ b/http/mdm/mdm.go
@@ -37,12 +37,21 @@ func CheckinHandler(svc service.Checkin, logger log.Logger) http.HandlerFunc {
 		}
 		respBytes, err := service.CheckinRequest(svc, mdmReqFromHTTPReq(r), bodyBytes)
 		if err != nil {
-			logger.Info("msg", "check-in request", "err", err)
+			logs := []interface{}{"msg", "check-in request", "err", err}
 			httpStatus := http.StatusInternalServerError
 			var statusErr *service.HTTPStatusError
 			if errors.As(err, &statusErr) {
 				httpStatus = statusErr.Status
+				err = statusErr.Unwrap()
 			}
+			// manualy unwrapping the `StatusErr` is not necessary as `errors.As` manually unwraps
+			var parseErr *mdm.ParseError
+			if errors.As(err, &parseErr) {
+				logs = append(logs, "content", string(parseErr.Content))
+				err = statusErr.Unwrap()
+			}
+			logs = append(logs, "http_status", httpStatus, "err", err)
+			logger.Info(logs...)
 			http.Error(w, http.StatusText(httpStatus), httpStatus)
 		}
 		w.Write(respBytes)
@@ -61,12 +70,20 @@ func CommandAndReportResultsHandler(svc service.CommandAndReportResults, logger
 		}
 		respBytes, err := service.CommandAndReportResultsRequest(svc, mdmReqFromHTTPReq(r), bodyBytes)
 		if err != nil {
-			logger.Info("msg", "command report results", "err", err)
+			logs := []interface{}{"msg", "command report results"}
 			httpStatus := http.StatusInternalServerError
 			var statusErr *service.HTTPStatusError
 			if errors.As(err, &statusErr) {
 				httpStatus = statusErr.Status
+				err = statusErr.Unwrap()
+			}
+			var parseErr *mdm.ParseError
+			if errors.As(err, &parseErr) {
+				logs = append(logs, "content", string(parseErr.Content))
+				err = parseErr.Unwrap()
 			}
+			logs = append(logs, "http_status", httpStatus, "err", err)
+			logger.Info(logs...)
 			http.Error(w, http.StatusText(httpStatus), httpStatus)
 		}
 		w.Write(respBytes)
diff --git a/mdm/checkin.go b/mdm/checkin.go
index 8f4e6ff..db9a38a 100644
--- a/mdm/checkin.go
+++ b/mdm/checkin.go
@@ -150,7 +150,7 @@ func DecodeCheckin(rawMessage []byte) (message interface{}, err error) {
 	w := &checkinUnmarshaller{raw: rawMessage}
 	err = plist.Unmarshal(rawMessage, w)
 	if err != nil {
-		err = &ParseError{Err: err, Body: rawMessage}
+		err = &ParseError{Err: err, Content: rawMessage}
 	}
 	message = w.message
 	return
diff --git a/mdm/command.go b/mdm/command.go
index c24fbd0..3be9998 100644
--- a/mdm/command.go
+++ b/mdm/command.go
@@ -35,7 +35,7 @@ func DecodeCommandResults(rawResults []byte) (results *CommandResults, err error
 	results = new(CommandResults)
 	err = plist.Unmarshal(rawResults, results)
 	if err != nil {
-		return nil, &ParseError{Err: err, Body: rawResults}
+		return nil, &ParseError{Err: err, Content: rawResults}
 	}
 	results.Raw = rawResults
 	if results.Status == "" {
@@ -58,7 +58,7 @@ func DecodeCommand(rawCommand []byte) (command *Command, err error) {
 	command = new(Command)
 	err = plist.Unmarshal(rawCommand, command)
 	if err != nil {
-		return nil, &ParseError{Err: err, Body: rawCommand}
+		return nil, &ParseError{Err: err, Content: rawCommand}
 	}
 	command.Raw = rawCommand
 	if command.CommandUUID == "" || command.Command.RequestType == "" {
diff --git a/mdm/mdm.go b/mdm/mdm.go
index 23c62b7..120871a 100644
--- a/mdm/mdm.go
+++ b/mdm/mdm.go
@@ -61,15 +61,18 @@ func (r *Request) Clone() *Request {
 	return r2
 }
 
+// ParseError represents a failure to parse an MDM structure (usually Apple Plist)
 type ParseError struct {
 	Err  error
-	Body []byte
+	Content []byte
 }
 
+// Unwrap returns the underlying error of the ParseError
 func (e *ParseError) Unwrap() error {
 	return e.Err
 }
 
+// Error formats the ParseError as a string
 func (e *ParseError) Error() string {
-	return fmt.Sprintf("parse error: %s: raw body: %v", e.Err, string(e.Body))
+	return fmt.Sprintf("parse error: %v: raw content: %v", e.Err, string(e.Content))
 }