Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for update by id #43

Merged
merged 4 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ kintone output plugin for Embulk stores app records from kintone.
- **max_sort_memory**: Maximum memory usage for sorting input records (bytes in long, default is the estimated available memory, which is the approximate value of the JVM's current free memory)
- **prefer_nulls**: Whether to set fields to null instead of default value of type when column is null (boolean, default is `false`)
- **ignore_nulls**: Whether to completely ignore fields when column is null (boolean, default is `false`)
- **skip_if_non_existing_id_or_update_key**: The skip policy if the record corresponding to the id or update key does not exist (string `auto`, `never` or `always`, default is `auto`). No effect for insert mode.
- **auto**:
- **update mode**: Skip the record if no id or update key value is specified.
- **upsert mode**: Skip the record if corresponds to the id does not exist or no update key value is specified.
- **never**: Never skip the record even if corresponds to the id or update key does not exist.
- **update mode**: Throw exception if no id or update key value is specified.
- **upsert mode**: Insert the record if corresponds to the id or update key does not exist (also, if no id or update key value is specified).
- **always**: Always skip the record if corresponds to the id or update key does not exist (also, if no id or update key value is specified). update mode and upsert mode will the same behavior (only updated, never inserted).
- **column_options** advanced: a key-value pairs where key is a column name and value is options for the column.
- **field_code**: field code (string, required)
- **type**: field type (string, required). See [this page](https://cybozu.dev/ja/kintone/docs/overview/field-types/#field-type-update) for list of available types. However, following types are not yet supported
Expand Down
117 changes: 117 additions & 0 deletions src/main/java/org/embulk/output/kintone/KintoneClient.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package org.embulk.output.kintone;

import com.kintone.client.KintoneClientBuilder;
import com.kintone.client.RecordClient;
import com.kintone.client.model.app.field.FieldProperty;
import com.kintone.client.model.record.FieldType;
import java.io.IOException;
import java.util.Map;
import java.util.function.Supplier;
import org.embulk.config.ConfigException;
import org.embulk.output.kintone.record.Id;
import org.embulk.output.kintone.util.Lazy;
import org.embulk.spi.Column;
import org.embulk.spi.Schema;
import org.embulk.spi.type.Type;
import org.embulk.spi.type.Types;

public class KintoneClient implements AutoCloseable {
private final PluginTask task;
private final Schema schema;
private final com.kintone.client.KintoneClient client;
private final Map<String, FieldProperty> fields;

public static Lazy<KintoneClient> lazy(Supplier<PluginTask> task, Schema schema) {
return new Lazy<KintoneClient>() {
@Override
protected KintoneClient initialValue() {
return new KintoneClient(task.get(), schema);
}
};
}

private KintoneClient(PluginTask task, Schema schema) {
this.task = task;
this.schema = schema;
KintoneClientBuilder builder = KintoneClientBuilder.create("https://" + task.getDomain());
if (task.getGuestSpaceId().isPresent()) {
builder.setGuestSpaceId(task.getGuestSpaceId().get());
}
if (task.getBasicAuthUsername().isPresent() && task.getBasicAuthPassword().isPresent()) {
builder.withBasicAuth(task.getBasicAuthUsername().get(), task.getBasicAuthPassword().get());
}
if (task.getUsername().isPresent() && task.getPassword().isPresent()) {
builder.authByPassword(task.getUsername().get(), task.getPassword().get());
} else if (task.getToken().isPresent()) {
builder.authByApiToken(task.getToken().get());
} else {
throw new ConfigException("Username and password or token must be configured.");
}
client = builder.build();
fields = client.app().getFormFields(task.getAppId());
KintoneMode.of(task).validate(task, this);
}

public void validateIdOrUpdateKey(String columnName) {
Column column = getColumn(columnName);
if (column == null) {
throw new ConfigException("The column '" + columnName + "' for update does not exist.");
}
validateId(column);
validateUpdateKey(column);
}

public Column getColumn(String columnName) {
return schema.getColumns().stream()
.filter(column -> column.getName().equals(columnName))
.findFirst()
.orElse(null);
}

public FieldType getFieldType(String fieldCode) {
FieldProperty field = fields.get(fieldCode);
return field == null ? null : field.getType();
}

public RecordClient record() {
return client.record();
}

@Override
public void close() {
try {
client.close();
} catch (IOException e) {
throw new RuntimeException("kintone throw exception", e);
}
}

private void validateId(Column column) {
if (!column.getName().equals(Id.FIELD)) {
return;
}
Type type = column.getType();
if (!type.equals(Types.LONG)) {
throw new ConfigException("The id column must be 'long'.");
}
}

private void validateUpdateKey(Column column) {
if (column.getName().equals(Id.FIELD)) {
return;
}
String fieldCode = getFieldCode(column);
FieldType fieldType = getFieldType(fieldCode);
if (fieldType == null) {
throw new ConfigException("The field '" + fieldCode + "' for update does not exist.");
}
if (fieldType != FieldType.SINGLE_LINE_TEXT && fieldType != FieldType.NUMBER) {
throw new ConfigException("The update_key must be 'SINGLE_LINE_TEXT' or 'NUMBER'.");
}
}

private String getFieldCode(Column column) {
KintoneColumnOption option = task.getColumnOptions().get(column.getName());
return option != null ? option.getFieldCode() : column.getName();
}
}
43 changes: 30 additions & 13 deletions src/main/java/org/embulk/output/kintone/KintoneColumnType.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.kintone.client.model.record.NumberFieldValue;
import com.kintone.client.model.record.OrganizationSelectFieldValue;
import com.kintone.client.model.record.RadioButtonFieldValue;
import com.kintone.client.model.record.Record;
import com.kintone.client.model.record.RichTextFieldValue;
import com.kintone.client.model.record.SingleLineTextFieldValue;
import com.kintone.client.model.record.SubtableFieldValue;
Expand All @@ -34,6 +35,8 @@
import org.embulk.spi.time.Timestamp;
import org.embulk.spi.type.Type;
import org.embulk.spi.type.Types;
import org.msgpack.value.ArrayValue;
import org.msgpack.value.StringValue;
import org.msgpack.value.Value;
import org.msgpack.value.ValueFactory;

Expand All @@ -49,6 +52,11 @@ public SingleLineTextFieldValue getFieldValue(String value, KintoneColumnOption
return new SingleLineTextFieldValue(value);
}

@Override
public String getValue(Record record, String fieldCode) {
return record.getSingleLineTextFieldValue(fieldCode);
}

@Override
public void setUpdateKey(UpdateKey updateKey, String field) {
updateKey.setField(field);
Expand All @@ -60,7 +68,7 @@ public void setUpdateKey(UpdateKey updateKey, String field, FieldValue value) {
}

@Override
public Value asValue(FieldValue value) {
public StringValue asValue(FieldValue value) {
return ValueFactory.newString(((SingleLineTextFieldValue) value).getValue());
}

Expand All @@ -81,7 +89,7 @@ public MultiLineTextFieldValue getFieldValue(String value, KintoneColumnOption o
}

@Override
public Value asValue(FieldValue value) {
public StringValue asValue(FieldValue value) {
return ValueFactory.newString(((MultiLineTextFieldValue) value).getValue());
}

Expand All @@ -102,7 +110,7 @@ public RichTextFieldValue getFieldValue(String value, KintoneColumnOption option
}

@Override
public Value asValue(FieldValue value) {
public StringValue asValue(FieldValue value) {
return ValueFactory.newString(((RichTextFieldValue) value).getValue());
}

Expand Down Expand Up @@ -132,6 +140,11 @@ public NumberFieldValue getFieldValue(Timestamp value, KintoneColumnOption optio
return (NumberFieldValue) getFieldValue(value.getEpochSecond(), option);
}

@Override
public BigDecimal getValue(Record record, String fieldCode) {
return record.getNumberFieldValue(fieldCode);
}

@Override
public void setUpdateKey(UpdateKey updateKey, String field) {
updateKey.setField(field);
Expand All @@ -143,8 +156,8 @@ public void setUpdateKey(UpdateKey updateKey, String field, FieldValue value) {
}

@Override
public Value asValue(FieldValue value) {
return ValueFactory.newString(((NumberFieldValue) value).getValue().toString());
public StringValue asValue(FieldValue value) {
return ValueFactory.newString(((NumberFieldValue) value).getValue().toPlainString());
}

@Override
Expand All @@ -164,7 +177,7 @@ public CheckBoxFieldValue getFieldValue(String value, KintoneColumnOption option
}

@Override
public Value asValue(FieldValue value) {
public ArrayValue asValue(FieldValue value) {
return ValueFactory.newArray(
((CheckBoxFieldValue) value)
.getValues().stream().map(ValueFactory::newString).collect(Collectors.toList()));
Expand All @@ -187,7 +200,7 @@ public RadioButtonFieldValue getFieldValue(String value, KintoneColumnOption opt
}

@Override
public Value asValue(FieldValue value) {
public StringValue asValue(FieldValue value) {
return ValueFactory.newString(((RadioButtonFieldValue) value).getValue());
}

Expand All @@ -208,7 +221,7 @@ public MultiSelectFieldValue getFieldValue(String value, KintoneColumnOption opt
}

@Override
public Value asValue(FieldValue value) {
public ArrayValue asValue(FieldValue value) {
return ValueFactory.newArray(
((MultiSelectFieldValue) value)
.getValues().stream().map(ValueFactory::newString).collect(Collectors.toList()));
Expand All @@ -231,7 +244,7 @@ public DropDownFieldValue getFieldValue(String value, KintoneColumnOption option
}

@Override
public Value asValue(FieldValue value) {
public StringValue asValue(FieldValue value) {
return ValueFactory.newString(((DropDownFieldValue) value).getValue());
}

Expand Down Expand Up @@ -336,7 +349,7 @@ public DateFieldValue getFieldValue(Timestamp value, KintoneColumnOption option)
}

@Override
public Value asValue(FieldValue value) {
public StringValue asValue(FieldValue value) {
return ValueFactory.newString(((DateFieldValue) value).getValue().toString());
}

Expand Down Expand Up @@ -378,7 +391,7 @@ public TimeFieldValue getFieldValue(Timestamp value, KintoneColumnOption option)
}

@Override
public Value asValue(FieldValue value) {
public StringValue asValue(FieldValue value) {
return ValueFactory.newString(((TimeFieldValue) value).getValue().toString());
}

Expand Down Expand Up @@ -416,7 +429,7 @@ public DateTimeFieldValue getFieldValue(Timestamp value, KintoneColumnOption opt
}

@Override
public Value asValue(FieldValue value) {
public StringValue asValue(FieldValue value) {
return ValueFactory.newString(((DateTimeFieldValue) value).getValue().toString());
}

Expand All @@ -437,7 +450,7 @@ public LinkFieldValue getFieldValue(String value, KintoneColumnOption option) {
}

@Override
public Value asValue(FieldValue value) {
public StringValue asValue(FieldValue value) {
return ValueFactory.newString(((LinkFieldValue) value).getValue());
}

Expand Down Expand Up @@ -540,6 +553,10 @@ public FieldValue getFieldValue(Value value, KintoneColumnOption option) {
}
}

public Object getValue(Record Record, String fieldCode) {
throw new UnsupportedOperationException();
}

public void setUpdateKey(UpdateKey updateKey, String field) {
throw new UnsupportedOperationException();
}
Expand Down
Loading
Loading