diff --git a/app.go b/app.go index 52d97fe..68ea1f0 100644 --- a/app.go +++ b/app.go @@ -316,6 +316,7 @@ func isJSON(contentType string) bool { func parseResponse(resp *http.Response) ([]byte, error) { body, err := ioutil.ReadAll(resp.Body) resp.Body.Close() + if err != nil { return nil, err } @@ -377,39 +378,53 @@ func (app *App) GetRecord(id uint64) (*Record, error) { return rec, nil } -// GetRecords fetches records matching given conditions. -// -// This method can retrieve up to 100 records at once. -// To retrieve more records, you need to call GetRecords with -// increasing "offset" query parameter until the number of records -// retrieved becomes less than 100. -// -// If fields is nil, all fields are retrieved. -// See API specs how to construct query strings. -func (app *App) GetRecords(fields []string, query string) ([]*Record, error) { +func (app *App) getRecords(fields []string, query string, totalCount bool) ([]*Record, string, error) { type request_body struct { - App uint64 `json:"app,string"` - Fields []string `json:"fields"` - Query string `json:"query"` + App uint64 `json:"app,string"` + Fields []string `json:"fields"` + Query string `json:"query"` + TotalCount bool `json:"totalCount"` } - data, _ := json.Marshal(request_body{app.AppId, fields, query}) + + data, _ := json.Marshal(request_body{app.AppId, fields, query, totalCount}) + req, err := app.newRequest("GET", "records", bytes.NewReader(data)) if err != nil { - return nil, err + return nil, "", err } resp, err := app.do(req) if err != nil { - return nil, err + return nil, "", err } body, err := parseResponse(resp) if err != nil { - return nil, err + return nil, "", err } - recs, err := DecodeRecords(body) + recs, respTotalCount, err := DecodeRecordsWithTotalCount(body) + if err != nil { - return nil, ErrInvalidResponse + return nil, "", ErrInvalidResponse } - return recs, nil + return recs, respTotalCount, nil +} + +// GetRecords fetches records matching given conditions. +// +// This method can retrieve up to 100 records at once. +// To retrieve more records, you need to call GetRecords with +// increasing "offset" query parameter until the number of records +// retrieved becomes less than 100. +// +// If fields is nil, all fields are retrieved. +// See API specs how to construct query strings. +func (app *App) GetRecords(fields []string, query string) ([]*Record, error) { + records, _, err := app.getRecords(fields, query, false) + return records, err +} + +// GetRecordsWithTotalCount fetches records matching given conditions and returns totalCount of query result. +func (app *App) GetRecordsWithTotalCount(fields []string, query string) ([]*Record, string, error) { + return app.getRecords(fields, query, true) } // GetAllRecords fetches all records. diff --git a/app_test.go b/app_test.go index 67574f5..df82a1b 100644 --- a/app_test.go +++ b/app_test.go @@ -7,6 +7,7 @@ package kintone import ( "crypto/tls" "encoding/base64" + "encoding/json" "fmt" "io" "io/ioutil" @@ -168,8 +169,30 @@ func handleResponseGetRecords(response http.ResponseWriter, request *http.Reques checkAuth(response, request) checkContentType(response, request) if request.Method == "GET" { - testData := GetTestDataGetRecords() - fmt.Fprint(response, testData.output) + type RequestBody struct { + App uint64 `json:"app,string"` + Fields []string `json:"fields"` + Query string `json:"query"` + TotalCount bool `json:"totalCount"` + } + + body, err := ioutil.ReadAll(request.Body) + if err != nil { + http.Error(response, "Bad request", http.StatusBadRequest) + return + } + var bodyRequest RequestBody + if err := json.Unmarshal([]byte(body), &bodyRequest); err != nil { + http.Error(response, "Body incorrect", http.StatusBadRequest) + } + + if bodyRequest.TotalCount { + testData := GetTestDataGetRecordsWithTotalCount() + fmt.Fprint(response, testData.output) + } else { + testData := GetTestDataGetRecords() + fmt.Fprint(response, testData.output) + } } else if request.Method == "DELETE" { testData := GetTestDataDeleteRecords() fmt.Fprint(response, testData.output) @@ -302,6 +325,22 @@ func TestGetRecord(t *testing.T) { } } +func TestGetRecordWithTotalCount(t *testing.T) { + testDataRecords := GetTestDataGetRecordsWithTotalCount() + app := newApp() + + if recs, totalCount, err := app.GetRecordsWithTotalCount(testDataRecords.input[0].([]string), testDataRecords.input[1].(string)); err != nil { + t.Error(err) + } else { + if len(recs) > 3 { + t.Error("Too many records") + } + if totalCount != "999" { + t.Error("TotalCount incorrect", err) + } + } +} + func TestUpdateRecord(t *testing.T) { testData := GetTestDataGetRecord() testDataRecords := GetTestDataGetRecords() diff --git a/app_test_json.go b/app_test_json.go index 39b2276..6583e22 100644 --- a/app_test_json.go +++ b/app_test_json.go @@ -161,6 +161,55 @@ func GetTestDataGetRecords() *TestData { } } +func GetTestDataGetRecordsWithTotalCount() *TestData { + return &TestData{ + input: []interface{}{ + []string{}, + "limit 3 offset 3", + }, + output: ` + { + "records":[ + { + "Created_datetime":{ + "type":"CREATED_TIME", + "value":"2019-03-11T04:50:00Z" + }, + "Created_by":{ + "type":"CREATOR", + "value":{ + "code":"Administrator", + "name":"Administrator" + } + }, + "$id":{ + "type":"__ID__", + "value":"1" + } + }, + { + "Created_datetime":{ + "type":"CREATED_TIME", + "value":"2019-03-11T06:42:00Z" + }, + "Created_by":{ + "type":"CREATOR", + "value":{ + "code":"Administrator", + "name":"Administrator" + } + }, + "$id":{ + "type":"__ID__", + "value":"2" + } + } + ], + "totalCount": "999" + }`, + } +} + func GetDataTestUploadFile() *TestData { return &TestData{ output: ` diff --git a/record.go b/record.go index ac4c8dc..cc45832 100644 --- a/record.go +++ b/record.go @@ -342,6 +342,28 @@ func DecodeRecords(b []byte) ([]*Record, error) { return rec_list, nil } +func DecodeRecordsWithTotalCount(b []byte) ([]*Record, string, error) { + var t struct { + Records []recordData `json:"records"` + TotalCount string `json:"totalCount"` + } + err := json.Unmarshal(b, &t) + if err != nil { + return nil, "", errors.New("Invalid JSON format") + } + + rec_list := make([]*Record, len(t.Records)) + for i, rd := range t.Records { + r, err := decodeRecordData(rd) + if err != nil { + return nil, "", err + } + rec_list[i] = r + } + + return rec_list, t.TotalCount, nil +} + // DecodeRecord decodes JSON response for single-get API. func DecodeRecord(b []byte) (*Record, error) { var t struct { diff --git a/record_test.go b/record_test.go index 238e149..dd2d5b9 100644 --- a/record_test.go +++ b/record_test.go @@ -389,3 +389,76 @@ func TestDecodeRecords(t *testing.T) { t.Error("dropdown must be invalid") } } + +func TestDecodeRecordsWithTotalCount(t *testing.T) { + b := []byte(` + { + "records": [ + { + "record_id": { + "type": "RECORD_NUMBER", + "value": "1" + }, + "created_time": { + "type": "CREATED_TIME", + "value": "2012-02-03T08:50:00Z" + }, + "updated_time": { + "type": "UPDATED_TIME", + "value": "2018-10-24T08:50:00Z" + }, + "dropdown": { + "type": "DROP_DOWN", + "value": null + } + }, + { + "record_id": { + "type": "RECORD_NUMBER", + "value": "2" + }, + "created_time": { + "type": "CREATED_TIME", + "value": "2012-02-03T09:22:00Z" + }, + "updated_time": { + "type": "UPDATED_TIME", + "value": "2018-10-24T09:22:00Z" + }, + "dropdown": { + "type": "DROP_DOWN", + "value": null + } + } + ], + "totalCount": "9999" + }`) + + type Record struct { + id uint64 + revision int64 + Fields map[string]interface{} + } + + rec, totalCount, err := DecodeRecordsWithTotalCount(b) + + if err != nil { + t.Fatal(err) + } + if totalCount != "9999" { + t.Error("totalCount is incorrect") + } + if len(rec) != 2 { + t.Error("length mismatch") + } + if _, ok := rec[0].Fields["record_id"]; !ok { + t.Error("record_id must exist") + } + dropdown, ok := rec[0].Fields["dropdown"] + if !ok { + t.Error("null dropdown field must exist") + } + if dropdown.(SingleSelectField).Valid { + t.Error("dropdown must be invalid") + } +}