From f36e21a65c758f86be6886dc9b392e5ddbb4c9cb Mon Sep 17 00:00:00 2001 From: James Hillyerd Date: Thu, 25 Feb 2016 19:21:47 -0800 Subject: [PATCH] REST APIv1 now uses lowercase JSON property names - Updated rest-apiv1.sh to pretty print JSON with jq if available - Fixed some missing checks on JSON testutils --- etc/rest-apiv1.sh | 12 +++++- rest/apiv1_controller.go | 32 ++++++++-------- rest/apiv1_controller_test.go | 20 +++++----- rest/testutils_test.go | 70 +++++++++++++++++++---------------- 4 files changed, 76 insertions(+), 58 deletions(-) diff --git a/etc/rest-apiv1.sh b/etc/rest-apiv1.sh index 39ad8a7..0a30433 100755 --- a/etc/rest-apiv1.sh +++ b/etc/rest-apiv1.sh @@ -36,6 +36,7 @@ arg_check() { main() { # Process options local curl_opts="" + local pretty="true" for arg in $*; do if [[ $arg == -* ]]; then case "$arg" in @@ -45,6 +46,7 @@ main() { ;; -i) curl_opts="$curl_opts -i" + pretty="" ;; **) echo "Unknown option: $arg" >&2 @@ -65,11 +67,13 @@ main() { local url="" local method="GET" + local is_json="" case "$command" in body) arg_check "$command" 2 $# url="$URL_ROOT/mailbox/$1/$2" + is_json="true" ;; delete) arg_check "$command" 2 $# @@ -79,6 +83,7 @@ main() { list) arg_check "$command" 1 $# url="$URL_ROOT/mailbox/$1" + is_json="true" ;; purge) arg_check "$command" 1 $# @@ -97,7 +102,12 @@ main() { ;; esac - curl $curl_opts -H "Accept: application/json" --noproxy "$API_HOST" -X "$method" "$url" + # Use jq to pretty-print if installed and we are expecting JSON output + if [ $pretty ] && [ $is_json ] && type -P jq; then + curl -s $curl_opts -H "Accept: application/json" --noproxy "$API_HOST" -X "$method" "$url" | jq . + else + curl -s $curl_opts -H "Accept: application/json" --noproxy "$API_HOST" -X "$method" "$url" + fi } if [ $# -lt 1 ]; then diff --git a/rest/apiv1_controller.go b/rest/apiv1_controller.go index 2dd0800..6cbdf29 100644 --- a/rest/apiv1_controller.go +++ b/rest/apiv1_controller.go @@ -14,30 +14,30 @@ import ( // JSONMessageHeaderV1 contains the basic header data for a message type JSONMessageHeaderV1 struct { - Mailbox string - ID string `json:"Id"` - From string - Subject string - Date time.Time - Size int64 + Mailbox string `json:"mailbox"` + ID string `json:"id"` + From string `json:"from"` + Subject string `json:"subject"` + Date time.Time `json:"date"` + Size int64 `json:"size"` } // JSONMessageV1 contains the same data as the header plus a JSONMessageBody type JSONMessageV1 struct { - Mailbox string - ID string `json:"Id"` - From string - Subject string - Date time.Time - Size int64 - Body *JSONMessageBodyV1 - Header mail.Header + Mailbox string `json:"mailbox"` + ID string `json:"id"` + From string `json:"from"` + Subject string `json:"subject"` + Date time.Time `json:"date"` + Size int64 `json:"size"` + Body *JSONMessageBodyV1 `json:"body"` + Header mail.Header `json:"header"` } // JSONMessageBodyV1 contains the Text and HTML versions of the message body type JSONMessageBodyV1 struct { - Text string - HTML string `json:"Html"` + Text string `json:"text"` + HTML string `json:"html"` } // MailboxListV1 renders a list of messages in a mailbox diff --git a/rest/apiv1_controller_test.go b/rest/apiv1_controller_test.go index a969c45..74aaa4e 100644 --- a/rest/apiv1_controller_test.go +++ b/rest/apiv1_controller_test.go @@ -16,16 +16,16 @@ const ( baseURL = "http://localhost/api/v1" // JSON map keys - mailboxKey = "Mailbox" - idKey = "Id" - fromKey = "From" - subjectKey = "Subject" - dateKey = "Date" - sizeKey = "Size" - headerKey = "Header" - bodyKey = "Body" - textKey = "Text" - htmlKey = "Html" + mailboxKey = "mailbox" + idKey = "id" + fromKey = "from" + subjectKey = "subject" + dateKey = "date" + sizeKey = "size" + headerKey = "header" + bodyKey = "body" + textKey = "text" + htmlKey = "html" ) func TestRestMailboxList(t *testing.T) { diff --git a/rest/testutils_test.go b/rest/testutils_test.go index 4a61dfb..d5f49a5 100644 --- a/rest/testutils_test.go +++ b/rest/testutils_test.go @@ -102,16 +102,20 @@ func (d *InputMessageData) CompareToJSONMessageMap(json interface{}) (errors []s if m, ok := json.(map[string]interface{}); ok { // Get nested body map - if body := m[bodyKey].(map[string]interface{}); ok { - if msg, ok := isJSONStringEqual(textKey, d.Text, body[textKey]); !ok { - errors = append(errors, msg) - } - if msg, ok := isJSONStringEqual(htmlKey, d.HTML, body[htmlKey]); !ok { - errors = append(errors, msg) + if m[bodyKey] != nil { + if body, ok := m[bodyKey].(map[string]interface{}); ok { + if msg, ok := isJSONStringEqual(textKey, d.Text, body[textKey]); !ok { + errors = append(errors, msg) + } + if msg, ok := isJSONStringEqual(htmlKey, d.HTML, body[htmlKey]); !ok { + errors = append(errors, msg) + } + } else { + panic(fmt.Sprintf("Expected map[string]interface{} in json key %q, got %T", + bodyKey, m[bodyKey])) } } else { - panic(fmt.Sprintf("Expected map[string]interface{} in json key %q, got %T", - bodyKey, m[bodyKey])) + errors = append(errors, fmt.Sprintf("Expected body in JSON %q but it was nil", bodyKey)) } exDate := d.Date.Format("2006-01-02T15:04:05.999999999-07:00") if msg, ok := isJSONStringEqual(dateKey, exDate, m[dateKey]); !ok { @@ -122,36 +126,40 @@ func (d *InputMessageData) CompareToJSONMessageMap(json interface{}) (errors []s } // Get nested header map - if header := m[headerKey].(map[string]interface{}); ok { - // Loop over input (expected) header names - for name, keyInputHeaders := range d.Header { - // Make sure expected header name exists in received JSON - if keyOutputVals, ok := header[name]; ok { - if keyOutputHeaders, ok := keyOutputVals.([]interface{}); ok { - // Loop over input (expected) header values - for _, inputHeader := range keyInputHeaders { - hasValue := false - // Look for expected value in received headers - for _, outputHeader := range keyOutputHeaders { - if inputHeader == outputHeader { - hasValue = true - break + if m[headerKey] != nil { + if header, ok := m[headerKey].(map[string]interface{}); ok { + // Loop over input (expected) header names + for name, keyInputHeaders := range d.Header { + // Make sure expected header name exists in received JSON + if keyOutputVals, ok := header[name]; ok { + if keyOutputHeaders, ok := keyOutputVals.([]interface{}); ok { + // Loop over input (expected) header values + for _, inputHeader := range keyInputHeaders { + hasValue := false + // Look for expected value in received headers + for _, outputHeader := range keyOutputHeaders { + if inputHeader == outputHeader { + hasValue = true + break + } + } + if !hasValue { + errors = append(errors, fmt.Sprintf( + "JSON %v[%q] missing value %q", headerKey, name, inputHeader)) } } - if !hasValue { - errors = append(errors, fmt.Sprintf( - "JSON %v[%q] missing value %q", headerKey, name, inputHeader)) - } + } else { + // keyOutputValues was not a slice of interface{} + panic(fmt.Sprintf("Expected []interface{} in %v[%q], got %T", headerKey, + name, keyOutputVals)) } } else { - // keyOutputValues was not a slice of interface{} - panic(fmt.Sprintf("Expected []interface{} in %v[%q], got %T", headerKey, - name, keyOutputVals)) + errors = append(errors, fmt.Sprintf("JSON %v missing key %q", headerKey, name)) } - } else { - errors = append(errors, fmt.Sprintf("JSON %v missing key %q", headerKey, name)) } } + } else { + errors = append(errors, fmt.Sprintf("Expected header in JSON %q but it was nil", headerKey)) } } else { panic(fmt.Sprintf("Expected map[string]interface{} in json, got %T", json))