diff --git a/src/main/java/com/kintone/client/KintoneApi.java b/src/main/java/com/kintone/client/KintoneApi.java index 3298dacb..8fe494b8 100644 --- a/src/main/java/com/kintone/client/KintoneApi.java +++ b/src/main/java/com/kintone/client/KintoneApi.java @@ -85,6 +85,7 @@ import com.kintone.client.api.record.UpdateRecordStatusResponseBody; import com.kintone.client.api.record.UpdateRecordStatusesResponseBody; import com.kintone.client.api.record.UpdateRecordsResponseBody; +import com.kintone.client.api.record.UpsertRecordsResponseBody; import com.kintone.client.api.schema.GetApiListResponseBody; import com.kintone.client.api.space.AddGuestsResponseBody; import com.kintone.client.api.space.AddSpaceFromTemplateResponseBody; @@ -112,6 +113,7 @@ public enum KintoneApi { GET_RECORDS(GET, "records", GetRecordsResponseBody.class), ADD_RECORDS(POST, "records", AddRecordsResponseBody.class), UPDATE_RECORDS(PUT, "records", UpdateRecordsResponseBody.class), + UPSERT_RECORDS(PUT, "records", UpsertRecordsResponseBody.class), DELETE_RECORDS(DELETE, "records", DeleteRecordsResponseBody.class), CREATE_CURSOR(POST, "records/cursor", CreateCursorResponseBody.class), GET_RECORDS_BY_CURSOR(GET, "records/cursor", GetRecordsByCursorResponseBody.class), diff --git a/src/main/java/com/kintone/client/RecordClient.java b/src/main/java/com/kintone/client/RecordClient.java index d568d9c2..8bebad7e 100644 --- a/src/main/java/com/kintone/client/RecordClient.java +++ b/src/main/java/com/kintone/client/RecordClient.java @@ -32,12 +32,15 @@ import com.kintone.client.api.record.UpdateRecordStatusesResponseBody; import com.kintone.client.api.record.UpdateRecordsRequest; import com.kintone.client.api.record.UpdateRecordsResponseBody; +import com.kintone.client.api.record.UpsertRecordsRequest; +import com.kintone.client.api.record.UpsertRecordsResponseBody; import com.kintone.client.model.Order; import com.kintone.client.model.record.PostedRecordComment; import com.kintone.client.model.record.Record; import com.kintone.client.model.record.RecordComment; import com.kintone.client.model.record.RecordForUpdate; import com.kintone.client.model.record.RecordRevision; +import com.kintone.client.model.record.RecordUpsertResult; import com.kintone.client.model.record.StatusAction; import com.kintone.client.model.record.UpdateKey; import java.util.List; @@ -535,6 +538,32 @@ public UpdateRecordsResponseBody updateRecords(UpdateRecordsRequest request) { return client.call(KintoneApi.UPDATE_RECORDS, request, handlers); } + /** + * Inserts or updates details of multiple records in an App, by specifying their record numbers, + * or their unique keys. If the record exists, it will be updated. If it does not exist, a new + * record will be created. + * + * @param app the App ID + * @param records a list of objects that include id/updateKey, revision and record objects + * @return a list of record update results. See {@link RecordUpsertResult} + */ + public List upsertRecords(long app, List records) { + UpsertRecordsRequest req = new UpsertRecordsRequest(); + req.setApp(app); + req.setRecords(records); + return upsertRecords(req).getRecords(); + } + + /** + * Inserts or updates details of multiple records in an App. + * + * @param request the request parameters. See {@link UpsertRecordsRequest} + * @return the response data. See {@link UpsertRecordsResponseBody} + */ + public UpsertRecordsResponseBody upsertRecords(UpsertRecordsRequest request) { + return client.call(KintoneApi.UPSERT_RECORDS, request, handlers); + } + /** * Updates the Status of a record of an App, that was set with the Process Management feature. * diff --git a/src/main/java/com/kintone/client/api/record/UpsertRecordsRequest.java b/src/main/java/com/kintone/client/api/record/UpsertRecordsRequest.java new file mode 100644 index 00000000..0a42691f --- /dev/null +++ b/src/main/java/com/kintone/client/api/record/UpsertRecordsRequest.java @@ -0,0 +1,23 @@ +package com.kintone.client.api.record; + +import com.kintone.client.api.KintoneRequest; +import com.kintone.client.model.record.RecordForUpdate; +import java.util.List; +import lombok.Data; + +/** A request object for Upsert Records API. */ +@Data +public class UpsertRecordsRequest implements KintoneRequest { + + /** The App ID (required). */ + private Long app; + + /** A list of objects that include id/updateKey, revision and record objects (required). */ + private List records; + + /** + * If set to true, the UPSERT mode will be enabled. In UPSERT mode, if the specified record + * exists, it will be updated. If it does not exist, a new record will be created (optional). + */ + private final Boolean upsert = true; +} diff --git a/src/main/java/com/kintone/client/api/record/UpsertRecordsResponseBody.java b/src/main/java/com/kintone/client/api/record/UpsertRecordsResponseBody.java new file mode 100644 index 00000000..8a1ac313 --- /dev/null +++ b/src/main/java/com/kintone/client/api/record/UpsertRecordsResponseBody.java @@ -0,0 +1,14 @@ +package com.kintone.client.api.record; + +import com.kintone.client.api.KintoneResponseBody; +import com.kintone.client.model.record.RecordUpsertResult; +import java.util.List; +import lombok.Value; + +/** A response object for Upsert Records API. */ +@Value +public class UpsertRecordsResponseBody implements KintoneResponseBody { + + /** A list of objects that include the record IDs, updated revisions, and operations. */ + private final List records; +} diff --git a/src/main/java/com/kintone/client/model/record/RecordOperationType.java b/src/main/java/com/kintone/client/model/record/RecordOperationType.java new file mode 100644 index 00000000..a86ea25d --- /dev/null +++ b/src/main/java/com/kintone/client/model/record/RecordOperationType.java @@ -0,0 +1,10 @@ +package com.kintone.client.model.record; + +/** The type of operation performed on a record when using UPSERT mode in Update Records API. */ +public enum RecordOperationType { + /** An existing record was updated. */ + UPDATE, + + /** A new record was created. */ + INSERT +} diff --git a/src/main/java/com/kintone/client/model/record/RecordUpsertResult.java b/src/main/java/com/kintone/client/model/record/RecordUpsertResult.java new file mode 100644 index 00000000..fd848ff4 --- /dev/null +++ b/src/main/java/com/kintone/client/model/record/RecordUpsertResult.java @@ -0,0 +1,20 @@ +package com.kintone.client.model.record; + +import lombok.Value; + +/** A value that contains the record ID, revision, and operation, returned by Upsert Records API. */ +@Value +public class RecordUpsertResult { + + /** The Record ID */ + private final long id; + + /** The revision number of the record after updating or creating the record. */ + private final long revision; + + /** + * The operation that was performed. UPDATE (existing record was updated) or INSERT (new record + * was created). Null when not using UPSERT mode. + */ + private final RecordOperationType operation; +} diff --git a/src/test/java/com/kintone/client/RecordClientTest.java b/src/test/java/com/kintone/client/RecordClientTest.java index 94fc8ec1..d46b6f9e 100644 --- a/src/test/java/com/kintone/client/RecordClientTest.java +++ b/src/test/java/com/kintone/client/RecordClientTest.java @@ -35,11 +35,15 @@ import com.kintone.client.api.record.UpdateRecordStatusesResponseBody; import com.kintone.client.api.record.UpdateRecordsRequest; import com.kintone.client.api.record.UpdateRecordsResponseBody; +import com.kintone.client.api.record.UpsertRecordsRequest; +import com.kintone.client.api.record.UpsertRecordsResponseBody; import com.kintone.client.model.Order; import com.kintone.client.model.record.Record; import com.kintone.client.model.record.RecordComment; import com.kintone.client.model.record.RecordForUpdate; +import com.kintone.client.model.record.RecordOperationType; import com.kintone.client.model.record.RecordRevision; +import com.kintone.client.model.record.RecordUpsertResult; import com.kintone.client.model.record.StatusAction; import com.kintone.client.model.record.UpdateKey; import java.util.Arrays; @@ -548,6 +552,40 @@ public void updateRecords_UpdateRecordsRequest() { assertThat(mockClient.getLastBody()).isEqualTo(req); } + @Test + public void upsertRecords_long_List() { + RecordForUpdate up1 = new RecordForUpdate(1L, new Record()); + RecordForUpdate up2 = new RecordForUpdate(new UpdateKey("text", "test"), new Record()); + RecordUpsertResult result1 = new RecordUpsertResult(1, 2, RecordOperationType.UPDATE); + RecordUpsertResult result2 = new RecordUpsertResult(100, 2, RecordOperationType.INSERT); + mockClient.setResponseBody(new UpsertRecordsResponseBody(Arrays.asList(result1, result2))); + + List records = Arrays.asList(up1, up2); + List result = sut.upsertRecords(1, records); + assertThat(result).hasSize(2); + assertThat(result.get(0).getId()).isEqualTo(1); + assertThat(result.get(0).getRevision()).isEqualTo(2); + assertThat(result.get(0).getOperation()).isEqualTo(RecordOperationType.UPDATE); + assertThat(result.get(1).getId()).isEqualTo(100); + assertThat(result.get(1).getRevision()).isEqualTo(2); + assertThat(result.get(1).getOperation()).isEqualTo(RecordOperationType.INSERT); + assertThat(mockClient.getLastApi()).isEqualTo(KintoneApi.UPSERT_RECORDS); + assertThat(mockClient.getLastBody()).isInstanceOf(UpsertRecordsRequest.class); + UpsertRecordsRequest expected = new UpsertRecordsRequest().setApp(1L).setRecords(records); + assertThat(mockClient.getLastBody()).usingRecursiveComparison().isEqualTo(expected); + } + + @Test + public void upsertRecords_UpsertRecordsRequest() { + UpsertRecordsRequest req = new UpsertRecordsRequest(); + UpsertRecordsResponseBody resp = new UpsertRecordsResponseBody(Collections.emptyList()); + mockClient.setResponseBody(resp); + + assertThat(sut.upsertRecords(req)).isEqualTo(resp); + assertThat(mockClient.getLastApi()).isEqualTo(KintoneApi.UPSERT_RECORDS); + assertThat(mockClient.getLastBody()).isEqualTo(req); + } + @Test public void updateRecordStatus_long_long_String() { mockClient.setResponseBody(new UpdateRecordStatusResponseBody(10L));