diff --git a/README.md b/README.md
index 82d5521..f37fc34 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@ to access [kintone][] with its official REST API ([en][APIen], [ja][APIja]).
## Version
-0.2.0
+0.3.0
## License
diff --git a/app.go b/app.go
index aace325..60007a9 100644
--- a/app.go
+++ b/app.go
@@ -24,8 +24,8 @@ import (
)
const (
- NAME = "kintone-go-SDK"
- VERSION = "0.2.0"
+ NAME = "go-kintone"
+ VERSION = "0.3.0"
DEFAULT_TIMEOUT = time.Second * 600 // Default value for App.Timeout
)
@@ -124,7 +124,7 @@ type App struct {
basicAuth bool // true to use Basic Authentication.
basicAuthUser string // User name for Basic Authentication.
basicAuthPassword string // Password for Basic Authentication.
- userAgentHeader string // User-agent request header string
+ extUserAgent string // User-agent request header string
}
// SetBasicAuth enables use of HTTP basic authentication for access
@@ -152,12 +152,71 @@ func (app *App) GetBasicAuthPassword() string {
// SetUserAgentHeader set custom user-agent header for http request
func (app *App) SetUserAgentHeader(userAgentHeader string) {
- app.userAgentHeader = userAgentHeader
+ app.extUserAgent = userAgentHeader
}
// GetUserAgentHeader get user-agent header string
func (app *App) GetUserAgentHeader() string {
- return app.userAgentHeader
+ userAgent := NAME + "/" + VERSION
+ if len(app.extUserAgent) > 0 {
+ return userAgent + " " + app.extUserAgent
+ }
+ return userAgent
+}
+
+func (app *App) createUrl(api string, query string) url.URL {
+ path := fmt.Sprintf("/k/v1/%s.json", api)
+ if app.GuestSpaceId > 0 {
+ path = fmt.Sprintf("/k/guest/%d/v1/%s.json", app.GuestSpaceId, api)
+ }
+
+ resultUrl := url.URL{
+ Scheme: "https",
+ Host: app.Domain,
+ Path: path,
+ }
+
+ if len(query) > 0 {
+ resultUrl.RawQuery = query
+ }
+ return resultUrl
+}
+func (app *App) setAuth(request *http.Request) {
+ if app.basicAuth {
+ request.SetBasicAuth(app.basicAuthUser, app.basicAuthPassword)
+ }
+
+ if len(app.ApiToken) > 0 {
+ request.Header.Set("X-Cybozu-API-Token", app.ApiToken)
+ }
+
+ if len(app.User) > 0 && len(app.Password) > 0 {
+ request.Header.Set("X-Cybozu-Authorization", base64.StdEncoding.EncodeToString(
+ []byte(app.User+":"+app.Password)))
+ }
+}
+
+//NewRequest create a request connect to kintone api.
+func (app *App) NewRequest(method, url string, body io.Reader) (*http.Request, error) {
+ bodyData := io.Reader(nil)
+ if body != nil {
+ bodyData = body
+ }
+
+ request, err := http.NewRequest(method, url, bodyData)
+ if err != nil {
+ return nil, err
+ }
+
+ request.Header.Set("User-Agent", app.GetUserAgentHeader())
+
+ if method != "GET" {
+ request.Header.Set("Content-Type", "application/json")
+ }
+
+ app.setAuth(request)
+
+ return request, nil
}
func (app *App) newRequest(method, api string, body io.Reader) (*http.Request, error) {
@@ -191,12 +250,8 @@ func (app *App) newRequest(method, api string, body io.Reader) (*http.Request, e
req.Header.Set("X-Cybozu-API-Token", app.ApiToken)
}
req.Header.Set("Content-Type", "application/json")
+ req.Header.Set("User-Agent", app.GetUserAgentHeader())
- if len(app.GetUserAgentHeader()) != 0 {
- req.Header.Set("User-Agent", app.userAgentHeader)
- } else {
- req.Header.Set("User-Agent", NAME+"/"+VERSION)
- }
return req, nil
}
@@ -1000,3 +1055,82 @@ func (app *App) Fields() (map[string]*FieldInfo, error) {
}
return ret, nil
}
+
+//CreateCursor return the meta data of the Cursor in this application
+func (app *App) CreateCursor(fields []string, query string, size uint64) (*Cursor, error) {
+ type cursor struct {
+ App uint64 `json:"app"`
+ Fields []string `json:"fields"`
+ Size uint64 `json:"size"`
+ Query string `json:"query"`
+ }
+ data := cursor{App: app.AppId, Fields: fields, Size: size, Query: query}
+ jsonData, _ := json.Marshal(data)
+ url := app.createUrl("records/cursor", "")
+ request, err := app.NewRequest("POST", url.String(), bytes.NewBuffer(jsonData))
+ if err != nil {
+ return nil, err
+ }
+ response, err := app.do(request)
+ if err != nil {
+ return nil, err
+ }
+ body, err := parseResponse(response)
+ if err != nil {
+ return nil, err
+ }
+ result, err := decodeCursor(body)
+ return result, nil
+}
+
+// DeleteCursor - Delete cursor by id
+func (app *App) DeleteCursor(id string) error {
+ type requestBody struct {
+ Id string `json:"id"`
+ }
+ data, err := json.Marshal(requestBody{Id: id})
+ if err != nil {
+ return err
+ }
+
+ url := app.createUrl("records/cursor", "")
+ request, err := app.NewRequest("DELETE", url.String(), bytes.NewBuffer(data))
+ if err != nil {
+ return err
+ }
+
+ response, err := app.do(request)
+ if err != nil {
+ return err
+ }
+
+ _, err = parseResponse(response)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+//Using Cursor Id to get all records
+//GetRecordsByCursor return the meta data of the Record in this application
+func (app *App) GetRecordsByCursor(id string) (*GetRecordsCursorResponse, error) {
+ url := app.createUrl("records/cursor", "id="+id)
+ request, err := app.NewRequest("GET", url.String(), nil)
+ if err != nil {
+ return nil, err
+ }
+ response, err := app.do(request)
+ if err != nil {
+ return nil, err
+ }
+ data, err := parseResponse(response)
+ if err != nil {
+ return nil, err
+ }
+ recordsCursorResponse, err := DecodeGetRecordsCursorResponse(data)
+ if err != nil {
+ return nil, err
+ }
+ return recordsCursorResponse, nil
+}
diff --git a/app_test.go b/app_test.go
index d9e8f7b..c1fe851 100644
--- a/app_test.go
+++ b/app_test.go
@@ -5,94 +5,231 @@
package kintone
import (
- "bytes"
+ "crypto/tls"
+ "encoding/base64"
+ "fmt"
+ "io"
"io/ioutil"
+ "net"
+ "net/http"
+ "net/http/httptest"
"os"
"strings"
"testing"
"time"
)
-func newApp(appID uint64) *App {
- return &App{
- Domain: os.Getenv("KINTONE_DOMAIN"),
- User: os.Getenv("KINTONE_USER"),
- Password: os.Getenv("KINTONE_PASSWORD"),
- AppId: appID,
+const (
+ KINTONE_DOMAIN = "localhost:8088"
+ KINTONE_USERNAME = "test"
+ KINTONE_PASSWORD = "test"
+ KINTONE_APP_ID = 1
+ KINTONE_API_TOKEN = "1e42da75-8432-4adb-9a2b-dbb6e7cb3c6b"
+ KINTONE_GUEST_SPACE_ID = 1
+ AUTH_HEADER_TOKEN = "X-Cybozu-API-Token"
+ AUTH_HEADER_PASSWORD = "X-Cybozu-Authorization"
+ AUTH_HEADER_BASIC = "Authorization"
+ CONTENT_TYPE = "Content-Type"
+ APPLICATION_JSON = "application/json"
+ BASIC_AUTH = true
+ BASIC_AUTH_USER = "basic"
+ BASIC_AUTH_PASSWORD = "basic"
+)
+
+func createServerTest(mux *http.ServeMux) (*httptest.Server, error) {
+ ts := httptest.NewUnstartedServer(mux)
+ listen, err := net.Listen("tcp", KINTONE_DOMAIN)
+
+ if err != nil {
+ return nil, err
}
+
+ ts.Listener.Close()
+ ts.Listener = listen
+ ts.StartTLS()
+ http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
+ return ts, nil
}
-func newAppWithApiToken(appId uint64) *App {
- return &App{
- Domain: os.Getenv("KINTONE_DOMAIN"),
- ApiToken: os.Getenv("KINTONE_API_TOKEN"),
- AppId: appId,
+func createServerMux() *http.ServeMux {
+ mux := http.NewServeMux()
+ mux.HandleFunc("/k/v1/record.json", handleResponseGetRecord)
+ mux.HandleFunc("/k/v1/records.json", handleResponseGetRecords)
+ mux.HandleFunc("/k/v1/record/comments.json", handleResponseGetRecordsComments)
+ mux.HandleFunc("/k/v1/file.json", handleResponseUploadFile)
+ mux.HandleFunc("/k/v1/record/comment.json", handleResponseRecordComments)
+ mux.HandleFunc("/k/v1/records/cursor.json", handleResponseRecordsCursor)
+ mux.HandleFunc("/k/v1/app/status.json", handleResponseProcess)
+ mux.HandleFunc("/k/v1/form.json", handleResponseForm)
+ mux.HandleFunc("/k/guest/1/v1/form.json", handleResponseForm)
+ return mux
+}
+
+// header check
+func checkAuth(response http.ResponseWriter, request *http.Request) {
+ authPassword := request.Header.Get(AUTH_HEADER_PASSWORD)
+ authToken := request.Header.Get(AUTH_HEADER_TOKEN)
+ authBasic := request.Header.Get(AUTH_HEADER_BASIC)
+
+ userAndPass := base64.StdEncoding.EncodeToString(
+ []byte(KINTONE_USERNAME + ":" + KINTONE_USERNAME))
+
+ userAndPassBasic := "Basic " + base64.StdEncoding.EncodeToString(
+ []byte(BASIC_AUTH_USER+":"+BASIC_AUTH_PASSWORD))
+
+ if authToken == "" && authPassword == "" && authBasic == "" {
+ http.Error(response, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
+ }
+ if BASIC_AUTH && authBasic != "" && authBasic != userAndPassBasic {
+ http.Error(response, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
+ }
+ if authToken != "" && authToken != KINTONE_API_TOKEN {
+ http.Error(response, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
+ }
+ if authPassword != "" && authPassword != userAndPass {
+ http.Error(response, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
}
}
-func newAppInGuestSpace(appId uint64, guestSpaceId uint64) *App {
- return &App{
- Domain: os.Getenv("KINTONE_DOMAIN"),
- User: os.Getenv("KINTONE_USER"),
- Password: os.Getenv("KINTONE_PASSWORD"),
- AppId: appId,
- GuestSpaceId: guestSpaceId,
+func checkContentType(response http.ResponseWriter, request *http.Request) {
+ contentType := request.Header.Get(CONTENT_TYPE)
+ if contentType != APPLICATION_JSON {
+ http.Error(response, http.StatusText(http.StatusNoContent), http.StatusNoContent)
}
}
-func TestGetRecord(t *testing.T) {
- a := newApp(4799)
- if len(a.Password) == 0 {
- t.Skip()
+// handler mux
+func handleResponseProcess(response http.ResponseWriter, request *http.Request) {
+ checkAuth(response, request)
+ TestData := GetTestDataProcess()
+ fmt.Fprint(response, TestData.output)
+}
+
+func handleResponseForm(response http.ResponseWriter, request *http.Request) {
+ checkAuth(response, request)
+ if request.Method == "GET" {
+ checkContentType(response, request)
+ testData := GetDataTestForm()
+ fmt.Fprint(response, testData.output)
}
+}
- if rec, err := a.GetRecord(116); err != nil {
- t.Error(err)
- } else {
- if rec.Id() != 116 {
- t.Errorf("Unexpected Id: %d", rec.Id())
- }
- for _, f := range rec.Fields {
- if files, ok := f.(FileField); ok {
- if len(files) == 0 {
- continue
- }
- fd, err := a.Download(files[0].FileKey)
- if err != nil {
- t.Error(err)
- } else {
- data, _ := ioutil.ReadAll(fd.Reader)
- t.Logf("%s %d bytes", fd.ContentType, len(data))
- }
- }
- }
+func handleResponseRecordsCursor(response http.ResponseWriter, request *http.Request) {
+ checkAuth(response, request)
+ if request.Method == "GET" {
+ testData := GetDataTestGetRecordsByCursor()
+ fmt.Fprint(response, testData.output)
+ } else if request.Method == "DELETE" {
+ checkContentType(response, request)
+ testData := GetTestDataDeleteCursor()
+ fmt.Fprint(response, testData.output)
+ } else if request.Method == "POST" {
+ checkContentType(response, request)
+ testData := GetTestDataCreateCursor()
+ fmt.Fprint(response, testData.output)
}
+}
- if recs, err := a.GetRecords(nil, "limit 3 offset 3"); err != nil {
- t.Error(err)
- } else {
- if len(recs) > 3 {
- t.Error("Too many records")
- }
+func handleResponseRecordComments(response http.ResponseWriter, request *http.Request) {
+ checkAuth(response, request)
+ checkContentType(response, request)
+ if request.Method == "POST" {
+ testData := GetTestDataAddRecordComment()
+ fmt.Fprint(response, testData.output)
+ } else if request.Method == "DELETE" {
+ testData := GetDataTestDeleteRecordComment()
+ fmt.Fprint(response, testData.output)
}
+}
- if recs, err := a.GetAllRecords([]string{"レコード番号"}); err != nil {
- t.Error(err)
- } else {
- t.Log(len(recs))
+func handleResponseUploadFile(response http.ResponseWriter, request *http.Request) {
+ checkAuth(response, request)
+ if request.Method == "POST" {
+ testData := GetDataTestUploadFile()
+ fmt.Fprint(response, testData.output)
}
}
-func TestAddRecord(t *testing.T) {
- a := newApp(9004)
- if len(a.Password) == 0 {
- t.Skip()
+func handleResponseGetRecord(response http.ResponseWriter, request *http.Request) {
+ checkAuth(response, request)
+ checkContentType(response, request)
+ if request.Method == "GET" {
+ testData := GetTestDataGetRecord()
+ fmt.Fprint(response, testData.output)
+ } else if request.Method == "PUT" {
+ testData := GetTestDataUpdateRecordByKey()
+ fmt.Fprint(response, testData.output)
+ } else if request.Method == "POST" {
+ testData := GetTestDataAddRecord()
+ fmt.Fprint(response, testData.output)
+ }
+}
+
+func handleResponseGetRecords(response http.ResponseWriter, request *http.Request) {
+ checkAuth(response, request)
+ checkContentType(response, request)
+ if request.Method == "GET" {
+ testData := GetTestDataGetRecords()
+ fmt.Fprint(response, testData.output)
+ } else if request.Method == "DELETE" {
+ testData := GetTestDataDeleteRecords()
+ fmt.Fprint(response, testData.output)
+ } else if request.Method == "POST" {
+ testData := GetTestDataAddRecords()
+ fmt.Fprint(response, testData.output)
+ }
+}
+
+func handleResponseGetRecordsComments(response http.ResponseWriter, request *http.Request) {
+ checkAuth(response, request)
+ checkContentType(response, request)
+ testData := GetDataTestRecordComments()
+ fmt.Fprint(response, testData.output)
+}
+
+func TestMain(m *testing.M) {
+ mux := createServerMux()
+ ts, err := createServerTest(mux)
+ if err != nil {
+ fmt.Println("createServerTest: ", err)
+ os.Exit(1)
+ }
+ code := m.Run()
+ ts.Close()
+ os.Exit(code)
+}
+
+func newApp() *App {
+ return &App{
+ Domain: KINTONE_DOMAIN,
+ User: KINTONE_USERNAME,
+ Password: KINTONE_PASSWORD,
+ AppId: KINTONE_APP_ID,
+ }
+}
+func newAppWithGuest() *App {
+ return &App{
+ Domain: KINTONE_DOMAIN,
+ AppId: KINTONE_APP_ID,
+ User: KINTONE_USERNAME,
+ Password: KINTONE_PASSWORD,
+ GuestSpaceId: KINTONE_GUEST_SPACE_ID,
+ }
+}
+func newAppWithToken() *App {
+ return &App{
+ AppId: KINTONE_APP_ID,
+ Domain: KINTONE_DOMAIN,
+ ApiToken: KINTONE_API_TOKEN,
}
+}
+
+func TestAddRecord(t *testing.T) {
+ testData := GetDataTestAddRecord()
+ app := newApp()
- fileKey, err := a.Upload("ほげ春巻.txta", "text/html",
- bytes.NewReader([]byte(`abc
-hoge
-`)))
+ fileKey, err := app.Upload(testData.input[0].(string), testData.input[2].(string),
+ testData.input[1].(io.Reader))
if err != nil {
t.Error("Upload failed", err)
}
@@ -103,11 +240,10 @@ func TestAddRecord(t *testing.T) {
{FileKey: fileKey},
},
})
- _, err = a.AddRecord(rec)
+ _, err = app.AddRecord(rec)
if err != nil {
t.Error("AddRecord failed", rec)
}
-
recs := []*Record{
NewRecord(map[string]interface{}{
"title": SingleLineTextField("multi add 1"),
@@ -116,68 +252,137 @@ func TestAddRecord(t *testing.T) {
"title": SingleLineTextField("multi add 2"),
}),
}
- ids, err := a.AddRecords(recs)
+ ids, err := app.AddRecords(recs)
if err != nil {
t.Error("AddRecords failed", recs)
} else {
t.Log(ids)
}
}
+func TestGetRecord(t *testing.T) {
+ testData := GetTestDataGetRecord()
+ testDataRecords := GetTestDataGetRecords()
+ app := newApp()
+ if rec, err := app.GetRecord(uint64(testData.input[0].(int))); err != nil {
+ t.Error(err)
+ } else {
+ if rec.Id() != 1 {
+ t.Errorf("Unexpected Id: %d", rec.Id())
+ }
+ for _, f := range rec.Fields {
+ if files, ok := f.(FileField); ok {
+ if len(files) == 0 {
+ continue
+ }
+ fd, err := app.Download(files[0].FileKey)
+ if err != nil {
+ t.Error(err)
+ } else {
+ data, _ := ioutil.ReadAll(fd.Reader)
+ t.Logf("%s %d bytes", fd.ContentType, len(data))
+ }
+ }
+ }
+ }
-func TestUpdateRecord(t *testing.T) {
- a := newApp(9004)
- if len(a.Password) == 0 {
- t.Skip()
+ if recs, err := app.GetRecords(testDataRecords.input[0].([]string), testDataRecords.input[1].(string)); err != nil {
+ t.Error(err)
+ } else {
+ if len(recs) > 3 {
+ t.Error("Too many records")
+ }
}
- rec, err := a.GetRecord(4)
+ if recs, err := app.GetAllRecords(testDataRecords.input[0].([]string)); err != nil {
+ t.Error(err)
+ } else {
+ t.Log(len(recs))
+ }
+
+}
+func TestUpdateRecord(t *testing.T) {
+ testData := GetTestDataGetRecord()
+ testDataRecords := GetTestDataGetRecords()
+ testDataRecordByKey := GetTestDataUpdateRecordByKey()
+
+ app := newApp()
+
+ rec, err := app.GetRecord(uint64(testData.input[0].(int)))
if err != nil {
t.Fatal(err)
}
rec.Fields["title"] = SingleLineTextField("new title")
- if err := a.UpdateRecord(rec, true); err != nil {
+ if err := app.UpdateRecord(rec, testData.input[1].(bool)); err != nil {
t.Error("UpdateRecord failed", err)
}
- if err := a.UpdateRecordByKey(rec, true, "key"); err != nil {
+ rec.Fields[testDataRecordByKey.input[1].(string)] = SingleLineTextField(` {
+ "field": "unique_key",
+ "value": "unique_code"
+ }`)
+ if err := app.UpdateRecordByKey(rec, testData.input[1].(bool), testDataRecordByKey.input[1].(string)); err != nil {
t.Error("UpdateRecordByKey failed", err)
}
-
- recs, err := a.GetRecords(nil, "limit 3")
+ recs, err := app.GetRecords(testDataRecords.input[0].([]string), testDataRecords.input[1].(string))
if err != nil {
t.Fatal(err)
}
+
for _, rec := range recs {
rec.Fields["title"] = SingleLineTextField(time.Now().String())
+ rec.Fields["key"] = SingleLineTextField(` {
+ "field": "unique_key",
+ "value": "unique_code"
+ }`)
}
- if err := a.UpdateRecords(recs, true); err != nil {
+ if err := app.UpdateRecords(recs, testData.input[1].(bool)); err != nil {
t.Error("UpdateRecords failed", err)
}
- if err := a.UpdateRecordsByKey(recs, true, "key"); err != nil {
+ if err := app.UpdateRecordsByKey(recs, testDataRecordByKey.input[2].(bool), testDataRecordByKey.input[1].(string)); err != nil {
t.Error("UpdateRecordsByKey failed", err)
}
}
func TestDeleteRecord(t *testing.T) {
- a := newApp(9004)
- if len(a.Password) == 0 {
- t.Skip()
+ testData := GetTestDataDeleteRecords()
+ app := newApp()
+ if err := app.DeleteRecords(testData.input[0].([]uint64)); err != nil {
+ t.Error("DeleteRecords failed", err)
}
+}
- ids := []uint64{6, 7}
- if err := a.DeleteRecords(ids); err != nil {
- t.Error("DeleteRecords failed", err)
+func TestGetRecordsByCursor(t *testing.T) {
+ testData := GetDataTestGetRecordsByCursor()
+ app := newApp()
+ _, err := app.GetRecordsByCursor(testData.input[0].(string))
+ if err != nil {
+ t.Errorf("TestGetCursor is failed: %v", err)
}
+
}
-func TestFields(t *testing.T) {
- a := newApp(8326)
- if len(a.Password) == 0 {
- t.Skip()
+func TestDeleteCursor(t *testing.T) {
+ testData := GetTestDataDeleteCursor()
+ app := newApp()
+ err := app.DeleteCursor(testData.input[0].(string))
+ if err != nil {
+ t.Errorf("TestDeleteCursor is failed: %v", err)
}
+}
+
+func TestCreateCursor(t *testing.T) {
+ testData := GetTestDataCreateCursor()
+ app := newApp()
+ _, err := app.CreateCursor(testData.input[0].([]string), testData.input[1].(string), uint64(testData.input[2].(int)))
+ if err != nil {
+ t.Errorf("TestCreateCurSor is failed: %v", err)
+ }
+}
- fi, err := a.Fields()
+func TestFields(t *testing.T) {
+ app := newApp()
+ fi, err := app.Fields()
if err != nil {
t.Error("Fields failed", err)
}
@@ -187,50 +392,43 @@ func TestFields(t *testing.T) {
}
func TestApiToken(t *testing.T) {
- a := newAppWithApiToken(9974)
- if len(a.ApiToken) == 0 {
- t.Skip()
- }
-
- _, err := a.Fields()
+ app := newAppWithToken()
+ _, err := app.Fields()
if err != nil {
t.Error("Api token failed", err)
}
}
func TestGuestSpace(t *testing.T) {
- a := newAppInGuestSpace(185, 9)
- if len(a.Password) == 0 {
- t.Skip()
- }
-
- _, err := a.Fields()
+ app := newAppWithGuest()
+ _, err := app.Fields()
if err != nil {
t.Error("GuestSpace failed", err)
}
}
func TestGetRecordComments(t *testing.T) {
- a := newApp(13)
- var offset uint64 = 5
- var limit uint64 = 10
- if rec, err := a.GetRecordComments(3, "asc", offset, limit); err != nil {
+ testData := GetDataTestRecordComments()
+ app := newApp()
+ if rec, err := app.GetRecordComments(uint64(testData.input[0].(int)), testData.input[1].(string), uint64(testData.input[2].(int)), uint64(testData.input[3].(int))); err != nil {
t.Error(err)
} else {
- if !strings.Contains(rec[0].Id, "6") {
- t.Errorf("the first comment id mismatch. expected is 6 but actual %v", rec[0].Id)
+ if !strings.Contains(rec[0].Id, "3") {
+ t.Errorf("the first comment id mismatch. expected is 3 but actual %v", rec[0].Id)
}
}
}
+
func TestAddRecordComment(t *testing.T) {
- appTest := newApp(12)
+ testData := GetTestDataAddRecordComment()
+ appTest := newApp()
mentionMemberCybozu := &ObjMention{Code: "cybozu", Type: ConstCommentMentionTypeUser}
mentionGroupAdmin := &ObjMention{Code: "Administrators", Type: ConstCommentMentionTypeGroup}
mentionDepartmentAdmin := &ObjMention{Code: "Admin", Type: ConstCommentMentionTypeDepartment}
var cmt Comment
cmt.Text = "Test comment 222"
cmt.Mentions = []*ObjMention{mentionGroupAdmin, mentionMemberCybozu, mentionDepartmentAdmin}
- cmtID, err := appTest.AddRecordComment(2, &cmt)
+ cmtID, err := appTest.AddRecordComment(uint64(testData.input[0].(int)), &cmt)
if err != nil {
t.Error(err)
@@ -240,13 +438,21 @@ func TestAddRecordComment(t *testing.T) {
}
func TestDeleteComment(t *testing.T) {
- appTest := newApp(4)
- var cmtID uint64 = 14
- err := appTest.DeleteComment(3, 12)
-
+ testData := GetDataTestDeleteRecordComment()
+ appTest := newApp()
+ err := appTest.DeleteComment(uint64(testData.input[0].(int)), uint64(testData.input[1].(int)))
if err != nil {
t.Error(err)
} else {
- t.Logf("The comment with id = %v has been deleted successefully!", cmtID)
+ t.Logf("The comment with id = %v has been deleted successefully!", uint64(testData.input[0].(int)))
+ }
+}
+
+func TestGetProcess(t *testing.T) {
+ TestData := GetTestDataProcess()
+ app := newApp()
+ _, err := app.GetProcess(TestData.input[0].(string))
+ if err != nil {
+ t.Error("TestGetProcess failed: ", err)
}
}
diff --git a/app_test_json.go b/app_test_json.go
new file mode 100644
index 0000000..f38d07d
--- /dev/null
+++ b/app_test_json.go
@@ -0,0 +1,419 @@
+package kintone
+
+import (
+ "bytes"
+)
+
+type TestData struct {
+ input []interface{}
+ output string
+}
+
+func GetTestDataProcess() *TestData {
+ return &TestData{
+ input: []interface{}{"en"},
+ output: `
+ {
+ "enable":true,
+ "states":{
+ "Not started":{
+ "name":"Not started",
+ "index":"0",
+ "assignee":{
+ "type":"ONE",
+ "entities":[]
+ }
+ },
+ "In progress":{
+ "name":"In progress",
+ "index":"1",
+ "assignee":{
+ "type":"ALL",
+ "entities":[
+ {
+ "entity":{
+ "type":"USER",
+ "code":"user1"
+ },
+ "includeSubs":false
+ },
+ {
+ "entity":{
+ "type":"FIELD_ENTITY",
+ "code":"creator"
+ },
+ "includeSubs":false
+ },
+ {
+ "entity":{
+ "type":"CUSTOM_FIELD",
+ "code":"Boss"
+ },
+ "includeSubs":false
+ }
+ ]
+ }
+ },
+ "Completed":{
+ "name":"Completed",
+ "index":"2",
+ "assignee":{
+ "type":"ONE",
+ "entities":[]
+ }
+ }
+ },
+ "actions":[
+ {
+ "name":"Start",
+ "from":"Not started",
+ "to":"In progress",
+ "filterCond":"Record_number = \"1\""
+ },
+ {
+ "name":"Complete",
+ "from":"In progress",
+ "to":"Completed",
+ "filterCond":""
+ }
+ ],
+ "revision":"3"
+ }`,
+ }
+}
+
+func GetTestDataDeleteRecords() *TestData {
+ return &TestData{
+ input: []interface{}{[]uint64{6, 7}},
+ output: `{}`,
+ }
+}
+func GetTestDataGetRecord() *TestData {
+ return &TestData{
+ input: []interface{}{1, true},
+ output: `
+ {
+ "record":{
+ "Updated_by":{
+ "type":"MODIFIER",
+ "value":{
+ "code":"Administrator",
+ "name":"Administrator"
+ },
+ "key":"hehehe"
+ },
+ "$id":{
+ "type":"__ID__",
+ "value":"1"
+ }
+ }
+ }`,
+ }
+}
+
+func GetTestDataGetRecords() *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":null
+ }`,
+ }
+}
+
+func GetDataTestUploadFile() *TestData {
+ return &TestData{
+ output: `
+ {
+ "app":3,
+ "id":6,
+ "record":{
+ "attached_file":{
+ "value":[
+ {
+ "fileKey":" c15b3870-7505-4ab6-9d8d-b9bdbc74f5d6"
+ }
+ ]
+ }
+ }
+ }`,
+ }
+}
+
+func GetDataTestRecordComments() *TestData {
+ return &TestData{
+ input: []interface{}{1, "asc", 0, 10},
+ output: `
+ {
+ "comments":[
+ {
+ "id":"3",
+ "text":"user14 Thank you! Looks great.",
+ "createdAt":"2016-05-09T18:29:05Z",
+ "creator":{
+ "code":"user13",
+ "name":"user13"
+ },
+ "mentions":[
+ {
+ "code":"user14",
+ "type":"USER"
+ }
+ ]
+ },
+ {
+ "id":"2",
+ "text":"user13 Global Sales APAC Taskforce \nHere is today's report.",
+ "createdAt":"2016-05-09T18:27:54Z",
+ "creator":{
+ "code":"user14",
+ "name":"user14"
+ },
+ "mentions":[
+ {
+ "code":"user13",
+ "type":"USER"
+ },
+ {
+ "code":"Global Sales_1BNZeQ",
+ "type":"ORGANIZATION"
+ },
+ {
+ "code":"APAC Taskforce_DJrvzu",
+ "type":"GROUP"
+ }
+ ]
+ }
+ ],
+ "older":false,
+ "newer":false
+ }`,
+ }
+}
+
+func GetDataTestForm() *TestData {
+ return &TestData{
+ output: `
+ {
+ "properties":[
+ {
+ "code":"string_1",
+ "defaultValue":"",
+ "expression":"",
+ "hideExpression":"false",
+ "maxLength":"64",
+ "minLength":null,
+ "label":"string_1",
+ "noLabel":"false",
+ "required":"true",
+ "type":"SINGLE_LINE_TEXT",
+ "unique":"true"
+ },
+ {
+ "code":"number_1",
+ "defaultValue":"12345",
+ "digit":"true",
+ "displayScale":"4",
+ "expression":"",
+ "maxValue":null,
+ "minValue":null,
+ "label":"number_1",
+ "noLabel":"true",
+ "required":"false",
+ "type":"NUMBER",
+ "unique":"false"
+ },
+ {
+ "code":"checkbox_1",
+ "defaultValue":[
+ "sample1",
+ "sample3"
+ ],
+ "label":"checkbox_1",
+ "noLabel":"false",
+ "options":[
+ "sample1",
+ "sample2",
+ "sample3"
+ ],
+ "required":"false",
+ "type":"CHECK_BOX"
+ }
+ ]
+ }`,
+ }
+}
+
+func GetDataTestDeleteRecordComment() *TestData {
+ return &TestData{
+ input: []interface{}{3, 14},
+ output: `{}`,
+ }
+}
+func GetTestDataAddRecord() *TestData {
+ return &TestData{
+ output: `{
+ "id": "1",
+ "revision": "1"
+ }`,
+ }
+}
+
+func GetTestDataAddRecords() *TestData {
+ return &TestData{
+ output: `
+ {
+ "ids": ["77","78"],
+ "revisions": ["1","1"]
+ }`,
+ }
+}
+
+func GetDataTestAddRecord() *TestData {
+ return &TestData{
+ input: []interface{}{
+ "ほげ春巻.txta",
+ bytes.NewReader([]byte(`abc
+ hoge
+ `)),
+ "text/html",
+ },
+ output: `
+ {
+ "id": "1",
+ "revision": "1"
+ }`,
+ }
+}
+func getDataTestCreateCursor() *TestData {
+ return &TestData{
+ output: `
+ {
+ "id": "9a9716fe-1394-4677-a1c7-2199a5d28215",
+ "totalCount": 123456
+ }`,
+ }
+
+}
+func GetDataTestGetRecordsByCursor() *TestData {
+
+ return &TestData{
+ input: []interface{}{"9a9716fe-1394-4677-a1c7-2199a5d28215"},
+ output: `
+ {
+ "records":[
+ {
+ "$id":{
+ "type":"__ID__",
+ "value":"1"
+ },
+ "Created_by":{
+ "type":"CREATOR",
+ "value":{
+ "code":"Administrator",
+ "name":"Administrator"
+ }
+ },
+ "Created_datetime":{
+ "type":"CREATED_TIME",
+ "value":"2019-05-23T04:50:00Z"
+ }
+ }
+ ],
+ "next":false
+ }`,
+ }
+}
+
+func GetTestDataDeleteCursor() *TestData {
+ return &TestData{
+ input: []interface{}{"9a9716fe-1394-4677-a1c7-2199a5d28215"},
+ output: `{}`,
+ }
+}
+
+func GetTestDataCreateCursor() *TestData {
+ return &TestData{
+ input: []interface{}{[]string{"$id", "date"}, "", 100},
+ output: `{"id":"9a9716fe-1394-4677-a1c7-2199a5d28215"}`,
+ }
+}
+
+func GetTestDataAddRecordComment() *TestData {
+ return &TestData{
+ input: []interface{}{2},
+ output: `{"id": "4"}`,
+ }
+}
+func GetTestDataUpdateRecordByKey() *TestData {
+ return &TestData{
+ input: []interface{}{2, "key", true},
+ output: `
+ {
+ "app":1,
+ "records":[
+ {
+ "updateKey":{
+ "field":"unique_key",
+ "value":"CODE123"
+ },
+ "record":{
+ "Text":{
+ "value":"Silver plates"
+ }
+ }
+ },
+ {
+ "updateKey":{
+ "field":"unique_key",
+ "value":"CODE456"
+ },
+ "record":{
+ "Text":{
+ "value":"The quick brown fox."
+ }
+ }
+ }
+ ]
+ }`,
+ }
+}
diff --git a/cursor.go b/cursor.go
new file mode 100644
index 0000000..9494987
--- /dev/null
+++ b/cursor.go
@@ -0,0 +1,39 @@
+package kintone
+
+import (
+ "encoding/json"
+)
+
+//Object Cursor structure
+type Cursor struct {
+ Id string `json:"id"`
+ TotalCount string `json:"totalCount"`
+}
+type GetRecordsCursorResponse struct {
+ Records []*Record `json:"records"`
+ Next bool `json:"next"`
+}
+
+//decodeCursor decodes JSON response for cursor api
+func decodeCursor(b []byte) (c *Cursor, err error) {
+ err = json.Unmarshal(b, &c)
+ if err != nil {
+ return nil, err
+ }
+ return c, nil
+}
+func DecodeGetRecordsCursorResponse(b []byte) (rc *GetRecordsCursorResponse, err error) {
+ var t struct {
+ Next bool `json:"next"`
+ }
+ err = json.Unmarshal(b, &t)
+ if err != nil {
+ return nil, err
+ }
+ listRecord, err := DecodeRecords(b)
+ if err != nil {
+ return nil, err
+ }
+ getRecordsCursorResponse := &GetRecordsCursorResponse{Records: listRecord, Next: t.Next}
+ return getRecordsCursorResponse, nil
+}
diff --git a/cursor_test.go b/cursor_test.go
new file mode 100644
index 0000000..112bb24
--- /dev/null
+++ b/cursor_test.go
@@ -0,0 +1,13 @@
+package kintone
+
+import (
+ "testing"
+)
+
+func TestDecodeCursor(t *testing.T) {
+ data := []byte(`{"id":"aaaaaaaaaaaaaaaaaa","totalCount":"null"}`)
+ _, err := decodeCursor(data)
+ if err != nil {
+ t.Errorf("TestDecodeCursor is failed: %v", err)
+ }
+}
diff --git a/record.go b/record.go
index 88f93b1..e2ec6c3 100644
--- a/record.go
+++ b/record.go
@@ -324,6 +324,7 @@ func DecodeRecords(b []byte) ([]*Record, error) {
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)