embulk-input-dynamodb 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/master.yml +34 -0
  3. data/.github/workflows/test.yml +30 -0
  4. data/.scalafmt.conf +5 -0
  5. data/CHANGELOG.md +49 -0
  6. data/README.md +204 -54
  7. data/build.gradle +53 -44
  8. data/example/config-deprecated.yml +20 -0
  9. data/example/config-query-as-json.yml +18 -0
  10. data/example/config-query.yml +22 -0
  11. data/example/config-scan.yml +18 -0
  12. data/example/prepare_dynamodb_table.sh +67 -0
  13. data/gradle/wrapper/gradle-wrapper.jar +0 -0
  14. data/gradle/wrapper/gradle-wrapper.properties +1 -2
  15. data/gradlew +67 -48
  16. data/gradlew.bat +20 -10
  17. data/{test/run_dynamodb_local.sh → run_dynamodb_local.sh} +2 -1
  18. data/settings.gradle +1 -0
  19. data/src/main/scala/org/embulk/input/dynamodb/DeprecatedDynamodbInputPlugin.scala +73 -0
  20. data/src/main/scala/org/embulk/input/dynamodb/DynamodbInputPlugin.scala +76 -25
  21. data/src/main/scala/org/embulk/input/dynamodb/PluginTask.scala +132 -32
  22. data/src/main/scala/org/embulk/input/dynamodb/aws/Aws.scala +44 -0
  23. data/src/main/scala/org/embulk/input/dynamodb/aws/AwsClientConfiguration.scala +37 -0
  24. data/src/main/scala/org/embulk/input/dynamodb/aws/AwsCredentials.scala +240 -0
  25. data/src/main/scala/org/embulk/input/dynamodb/aws/AwsDynamodbConfiguration.scala +35 -0
  26. data/src/main/scala/org/embulk/input/dynamodb/aws/AwsEndpointConfiguration.scala +79 -0
  27. data/src/main/scala/org/embulk/input/dynamodb/aws/HttpProxy.scala +61 -0
  28. data/src/main/scala/org/embulk/input/dynamodb/deprecated/AttributeValueHelper.scala +72 -0
  29. data/src/main/scala/org/embulk/input/dynamodb/{Filter.scala → deprecated/Filter.scala} +3 -3
  30. data/src/main/scala/org/embulk/input/dynamodb/{FilterConfig.scala → deprecated/FilterConfig.scala} +13 -13
  31. data/src/main/scala/org/embulk/input/dynamodb/{ope → deprecated/ope}/AbstractOperation.scala +36 -18
  32. data/src/main/scala/org/embulk/input/dynamodb/{ope → deprecated/ope}/QueryOperation.scala +21 -13
  33. data/src/main/scala/org/embulk/input/dynamodb/{ope → deprecated/ope}/ScanOperation.scala +20 -13
  34. data/src/main/scala/org/embulk/input/dynamodb/item/DynamodbAttributeValue.scala +154 -0
  35. data/src/main/scala/org/embulk/input/dynamodb/item/DynamodbAttributeValueEmbulkTypeTransformable.scala +245 -0
  36. data/src/main/scala/org/embulk/input/dynamodb/item/DynamodbAttributeValueType.scala +33 -0
  37. data/src/main/scala/org/embulk/input/dynamodb/item/DynamodbItemColumnVisitor.scala +50 -0
  38. data/src/main/scala/org/embulk/input/dynamodb/item/DynamodbItemConsumer.scala +40 -0
  39. data/src/main/scala/org/embulk/input/dynamodb/item/DynamodbItemIterator.scala +19 -0
  40. data/src/main/scala/org/embulk/input/dynamodb/item/DynamodbItemReader.scala +64 -0
  41. data/src/main/scala/org/embulk/input/dynamodb/item/DynamodbItemSchema.scala +135 -0
  42. data/src/main/scala/org/embulk/input/dynamodb/operation/AbstractDynamodbOperation.scala +169 -0
  43. data/src/main/scala/org/embulk/input/dynamodb/operation/DynamodbOperationProxy.scala +59 -0
  44. data/src/main/scala/org/embulk/input/dynamodb/operation/DynamodbQueryOperation.scala +72 -0
  45. data/src/main/scala/org/embulk/input/dynamodb/operation/DynamodbScanOperation.scala +93 -0
  46. data/src/main/scala/org/embulk/input/dynamodb/operation/EmbulkDynamodbOperation.scala +15 -0
  47. data/src/main/scala/org/embulk/input/dynamodb/package.scala +4 -9
  48. data/src/test/scala/org/embulk/input/dynamodb/AttributeValueHelperTest.scala +245 -101
  49. data/src/test/scala/org/embulk/input/dynamodb/AwsCredentialsTest.scala +150 -97
  50. data/src/test/scala/org/embulk/input/dynamodb/DynamodbQueryOperationTest.scala +188 -0
  51. data/src/test/scala/org/embulk/input/dynamodb/DynamodbScanOperationTest.scala +181 -0
  52. data/src/test/scala/org/embulk/input/dynamodb/testutil/EmbulkTestBase.scala +85 -0
  53. metadata +73 -49
  54. data/circle.yml +0 -16
  55. data/config/checkstyle/checkstyle.xml +0 -128
  56. data/config/checkstyle/default.xml +0 -108
  57. data/src/main/scala/org/embulk/input/dynamodb/AttributeValueHelper.scala +0 -41
  58. data/src/main/scala/org/embulk/input/dynamodb/AwsCredentials.scala +0 -63
  59. data/src/main/scala/org/embulk/input/dynamodb/DynamoDBClient.scala +0 -23
  60. data/src/test/resources/yaml/authMethodBasic.yml +0 -21
  61. data/src/test/resources/yaml/authMethodBasic_Error.yml +0 -19
  62. data/src/test/resources/yaml/authMethodEnv.yml +0 -19
  63. data/src/test/resources/yaml/authMethodProfile.yml +0 -20
  64. data/src/test/resources/yaml/dynamodb-local-query.yml +0 -25
  65. data/src/test/resources/yaml/dynamodb-local-scan.yml +0 -23
  66. data/src/test/resources/yaml/notSetAuthMethod.yml +0 -20
  67. data/src/test/scala/org/embulk/input/dynamodb/ope/QueryOperationTest.scala +0 -83
  68. data/src/test/scala/org/embulk/input/dynamodb/ope/ScanOperationTest.scala +0 -83
  69. data/test/create_table.sh +0 -16
  70. data/test/put_items.sh +0 -25
@@ -0,0 +1,33 @@
1
+ package org.embulk.input.dynamodb.item
2
+
3
+ sealed abstract class DynamodbAttributeValueType
4
+
5
+ object DynamodbAttributeValueType {
6
+ final case object S extends DynamodbAttributeValueType
7
+ final case object N extends DynamodbAttributeValueType
8
+ final case object B extends DynamodbAttributeValueType
9
+ final case object SS extends DynamodbAttributeValueType
10
+ final case object NS extends DynamodbAttributeValueType
11
+ final case object BS extends DynamodbAttributeValueType
12
+ final case object M extends DynamodbAttributeValueType
13
+ final case object L extends DynamodbAttributeValueType
14
+ final case object NULL extends DynamodbAttributeValueType
15
+ final case object BOOL extends DynamodbAttributeValueType
16
+ final case object UNKNOWN extends DynamodbAttributeValueType
17
+
18
+ def apply(typeName: String): DynamodbAttributeValueType = {
19
+ typeName match {
20
+ case "S" => S
21
+ case "N" => N
22
+ case "B" => B
23
+ case "SS" => SS
24
+ case "NS" => NS
25
+ case "BS" => BS
26
+ case "M" => M
27
+ case "L" => L
28
+ case "NULL" => NULL
29
+ case "BOOL" => BOOL
30
+ case _ => UNKNOWN
31
+ }
32
+ }
33
+ }
@@ -0,0 +1,50 @@
1
+ package org.embulk.input.dynamodb.item
2
+
3
+ import org.embulk.spi.{Column, ColumnVisitor, PageBuilder}
4
+
5
+ case class DynamodbItemColumnVisitor(
6
+ itemReader: DynamodbItemReader,
7
+ pageBuilder: PageBuilder
8
+ ) extends ColumnVisitor {
9
+
10
+ override def booleanColumn(column: Column): Unit = {
11
+ itemReader.getBoolean(column) match {
12
+ case Some(v) => pageBuilder.setBoolean(column, v)
13
+ case None => pageBuilder.setNull(column)
14
+ }
15
+ }
16
+
17
+ override def longColumn(column: Column): Unit = {
18
+ itemReader.getLong(column) match {
19
+ case Some(v) => pageBuilder.setLong(column, v)
20
+ case None => pageBuilder.setNull(column)
21
+ }
22
+ }
23
+
24
+ override def doubleColumn(column: Column): Unit =
25
+ itemReader.getDouble(column) match {
26
+ case Some(v) => pageBuilder.setDouble(column, v)
27
+ case None => pageBuilder.setNull(column)
28
+ }
29
+
30
+ override def stringColumn(column: Column): Unit = {
31
+ itemReader.getString(column) match {
32
+ case Some(v) => pageBuilder.setString(column, v)
33
+ case None => pageBuilder.setNull(column)
34
+ }
35
+ }
36
+
37
+ override def timestampColumn(column: Column): Unit = {
38
+ itemReader.getTimestamp(column) match {
39
+ case Some(v) => pageBuilder.setTimestamp(column, v)
40
+ case None => pageBuilder.setNull(column)
41
+ }
42
+ }
43
+
44
+ override def jsonColumn(column: Column): Unit =
45
+ itemReader.getJson(column) match {
46
+ case Some(v) => pageBuilder.setJson(column, v)
47
+ case None => pageBuilder.setNull(column)
48
+ }
49
+
50
+ }
@@ -0,0 +1,40 @@
1
+ package org.embulk.input.dynamodb.item
2
+
3
+ import com.amazonaws.services.dynamodbv2.model.AttributeValue
4
+ import org.embulk.spi.PageBuilder
5
+
6
+ object DynamodbItemConsumer {
7
+
8
+ def consumeItemsAsJson(
9
+ schema: DynamodbItemSchema,
10
+ pageBuilder: PageBuilder
11
+ ): Seq[Map[String, AttributeValue]] => Unit = {
12
+ val column = schema.getEmbulkSchema.getColumn(0)
13
+ items: Seq[Map[String, AttributeValue]] =>
14
+ items.foreach { item =>
15
+ val transformable = DynamodbAttributeValueEmbulkTypeTransformable(
16
+ DynamodbAttributeValue(item)
17
+ )
18
+ transformable.asMessagePack match {
19
+ case Some(v) => pageBuilder.setJson(column, v)
20
+ case None => pageBuilder.setNull(column)
21
+ }
22
+ pageBuilder.addRecord()
23
+ }
24
+ }
25
+
26
+ def consumeItemsByEmbulkSchema(
27
+ schema: DynamodbItemSchema,
28
+ pageBuilder: PageBuilder
29
+ ): Seq[Map[String, AttributeValue]] => Unit = {
30
+ items: Seq[Map[String, AttributeValue]] =>
31
+ val itemReader = DynamodbItemReader(schema, DynamodbItemIterator(items))
32
+ val visitor = DynamodbItemColumnVisitor(itemReader, pageBuilder)
33
+
34
+ while (itemReader.nextItem) {
35
+ schema.visitColumns(visitor)
36
+ pageBuilder.addRecord()
37
+ }
38
+ }
39
+
40
+ }
@@ -0,0 +1,19 @@
1
+ package org.embulk.input.dynamodb.item
2
+
3
+ import com.amazonaws.services.dynamodbv2.model.AttributeValue
4
+
5
+ object DynamodbItemIterator {
6
+
7
+ def apply(data: Seq[Map[String, AttributeValue]]): DynamodbItemIterator =
8
+ new DynamodbItemIterator {
9
+
10
+ private val delegated: Iterator[Map[String, AttributeValue]] =
11
+ data.iterator
12
+ override def hasNext: Boolean = delegated.hasNext
13
+
14
+ override def next(): Map[String, DynamodbAttributeValue] =
15
+ delegated.next().map(x => (x._1, DynamodbAttributeValue(x._2)))
16
+ }
17
+ }
18
+
19
+ trait DynamodbItemIterator extends Iterator[Map[String, DynamodbAttributeValue]]
@@ -0,0 +1,64 @@
1
+ package org.embulk.input.dynamodb.item
2
+
3
+ import org.embulk.spi.Column
4
+ import org.embulk.spi.time.Timestamp
5
+ import org.msgpack.value.Value
6
+
7
+ case class DynamodbItemReader(
8
+ private val schema: DynamodbItemSchema,
9
+ private val ite: DynamodbItemIterator
10
+ ) {
11
+ private var currentItem: Map[String, DynamodbAttributeValue] = _
12
+
13
+ def nextItem: Boolean = {
14
+ ite.nextOption() match {
15
+ case Some(v) =>
16
+ currentItem = v
17
+ true
18
+ case None => false
19
+ }
20
+ }
21
+
22
+ def getSchema: DynamodbItemSchema = schema
23
+
24
+ def getTransformable(
25
+ name: String,
26
+ value: DynamodbAttributeValue
27
+ ): DynamodbAttributeValueEmbulkTypeTransformable = {
28
+ DynamodbAttributeValueEmbulkTypeTransformable(
29
+ value,
30
+ typeEnforcer = schema.getAttributeType(name),
31
+ timestampParser = schema.getTimestampParser(name)
32
+ )
33
+ }
34
+
35
+ def getBoolean(column: Column): Option[Boolean] =
36
+ currentItem
37
+ .get(column.getName)
38
+ .flatMap(v => getTransformable(column.getName, v).asBoolean)
39
+
40
+ def getString(column: Column): Option[String] =
41
+ currentItem
42
+ .get(column.getName)
43
+ .flatMap(v => getTransformable(column.getName, v).asString)
44
+
45
+ def getLong(column: Column): Option[Long] =
46
+ currentItem
47
+ .get(column.getName)
48
+ .flatMap(v => getTransformable(column.getName, v).asLong)
49
+
50
+ def getDouble(column: Column): Option[Double] =
51
+ currentItem
52
+ .get(column.getName)
53
+ .flatMap(v => getTransformable(column.getName, v).asDouble)
54
+
55
+ def getTimestamp(column: Column): Option[Timestamp] =
56
+ currentItem
57
+ .get(column.getName)
58
+ .flatMap(v => getTransformable(column.getName, v).asTimestamp)
59
+
60
+ def getJson(column: Column): Option[Value] =
61
+ currentItem
62
+ .get(column.getName)
63
+ .flatMap(v => getTransformable(column.getName, v).asMessagePack)
64
+ }
@@ -0,0 +1,135 @@
1
+ package org.embulk.input.dynamodb.item
2
+
3
+ import java.util.{Optional, List => JList}
4
+
5
+ import com.amazonaws.services.dynamodbv2.model.AttributeValue
6
+ import com.fasterxml.jackson.annotation.{JsonCreator, JsonValue}
7
+ import org.embulk.config.{Config, ConfigDefault, Task => EmbulkTask}
8
+ import org.embulk.spi.{Column, PageBuilder, Schema}
9
+ import org.embulk.spi.`type`.{Type, Types}
10
+ import org.embulk.spi.time.TimestampParser
11
+
12
+ import scala.jdk.CollectionConverters._
13
+ import scala.util.chaining._
14
+ import scala.util.Try
15
+
16
+ object DynamodbItemSchema {
17
+
18
+ trait ColumnTask
19
+ extends EmbulkTask
20
+ with TimestampParser.TimestampColumnOption {
21
+
22
+ @Config("name")
23
+ def getName: String
24
+
25
+ @Config("type")
26
+ def getType: Type
27
+
28
+ @Config("attribute_type")
29
+ @ConfigDefault("null")
30
+ def getAttributeType: Optional[String]
31
+ }
32
+
33
+ @deprecated(
34
+ message = "for DeprecatedDynamodbInputPlugin",
35
+ since = "0.3.0"
36
+ )
37
+ case class SchemaConfigCompat(columnTasks: Seq[ColumnTask]) {
38
+ @JsonCreator
39
+ def this(columnTasks: JList[ColumnTask]) =
40
+ this(columnTasks.asScala.toSeq)
41
+
42
+ @JsonValue
43
+ def getColumnTasks: JList[ColumnTask] = columnTasks.asJava
44
+
45
+ def toSchema: Schema =
46
+ Schema
47
+ .builder()
48
+ .tap { b =>
49
+ columnTasks.foreach { t =>
50
+ b.add(t.getName, t.getType)
51
+ }
52
+ }
53
+ .build()
54
+
55
+ def isEmpty: Boolean = columnTasks.isEmpty
56
+ }
57
+
58
+ trait Task extends EmbulkTask with TimestampParser.Task {
59
+
60
+ @Config("json_column_name")
61
+ @ConfigDefault("\"record\"")
62
+ def getJsonColumnName: String
63
+
64
+ @Config("columns")
65
+ @ConfigDefault("[]")
66
+ def getColumns: SchemaConfigCompat
67
+ }
68
+
69
+ }
70
+
71
+ case class DynamodbItemSchema(task: DynamodbItemSchema.Task) {
72
+
73
+ // TODO: build in this class after removing SchemaConfigCompat.
74
+ private lazy val embulkSchema: Schema =
75
+ if (!isItemAsJson) task.getColumns.toSchema
76
+ else
77
+ Schema
78
+ .builder()
79
+ .add(task.getJsonColumnName, Types.JSON)
80
+ .build()
81
+
82
+ private lazy val timestampParsers: Map[String, TimestampParser] =
83
+ task.getColumns.columnTasks.map { columnTask =>
84
+ columnTask.getName -> TimestampParser.of(task, columnTask)
85
+ }.toMap
86
+
87
+ private lazy val attributeTypes: Map[String, DynamodbAttributeValueType] =
88
+ task.getColumns.columnTasks
89
+ .filter(_.getAttributeType.isPresent)
90
+ .map { columnTask =>
91
+ columnTask.getName -> DynamodbAttributeValueType(
92
+ columnTask.getAttributeType.get()
93
+ )
94
+ }
95
+ .toMap
96
+
97
+ private lazy val embulkColumns: Map[String, Column] =
98
+ getEmbulkSchema.getColumns.asScala
99
+ .map(column => column.getName -> column)
100
+ .toMap
101
+
102
+ def getEmbulkSchema: Schema = embulkSchema
103
+
104
+ def getTimestampParser(column: Column): Option[TimestampParser] =
105
+ timestampParsers.get(column.getName)
106
+
107
+ def getTimestampParser(columnName: String): Option[TimestampParser] =
108
+ getEmbulkColumn(columnName).flatMap(getTimestampParser)
109
+
110
+ def getAttributeType(column: Column): Option[DynamodbAttributeValueType] =
111
+ attributeTypes.get(column.getName)
112
+
113
+ def getAttributeType(
114
+ columnName: String
115
+ ): Option[DynamodbAttributeValueType] =
116
+ getEmbulkColumn(columnName).flatMap(getAttributeType)
117
+
118
+ def getEmbulkColumn(columnName: String): Option[Column] =
119
+ embulkColumns.get(columnName)
120
+
121
+ def getEmbulkColumn(columnIndex: Int): Option[Column] =
122
+ Try(getEmbulkSchema.getColumn(columnIndex)).toOption
123
+
124
+ def isItemAsJson: Boolean = task.getColumns.isEmpty
125
+
126
+ def visitColumns(visitor: DynamodbItemColumnVisitor): Unit =
127
+ getEmbulkSchema.visitColumns(visitor)
128
+
129
+ def getItemsConsumer(
130
+ pageBuilder: PageBuilder
131
+ ): Seq[Map[String, AttributeValue]] => Unit = {
132
+ if (isItemAsJson) DynamodbItemConsumer.consumeItemsAsJson(this, pageBuilder)
133
+ else DynamodbItemConsumer.consumeItemsByEmbulkSchema(this, pageBuilder)
134
+ }
135
+ }
@@ -0,0 +1,169 @@
1
+ package org.embulk.input.dynamodb.operation
2
+
3
+ import java.lang.{
4
+ Boolean => JBoolean,
5
+ Integer => JInteger,
6
+ Long => JLong,
7
+ String => JString
8
+ }
9
+ import java.util.{Optional, Map => JMap}
10
+
11
+ import com.amazonaws.services.dynamodbv2.model.{
12
+ AttributeValue,
13
+ ReturnConsumedCapacity,
14
+ Select
15
+ }
16
+ import org.embulk.config.{
17
+ Config,
18
+ ConfigDefault,
19
+ ConfigException,
20
+ Task => EmbulkTask
21
+ }
22
+ import org.embulk.input.dynamodb.item.DynamodbAttributeValue
23
+
24
+ import scala.jdk.CollectionConverters._
25
+ import scala.language.reflectiveCalls
26
+
27
+ object AbstractDynamodbOperation {
28
+
29
+ trait Task extends EmbulkTask {
30
+
31
+ // ref. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html#Query.ReadConsistency
32
+ // ref. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Scan.html#Scan.ReadConsistency
33
+ @Config("consistent_read")
34
+ @ConfigDefault("false")
35
+ def getConsistentRead: Boolean
36
+
37
+ // ref. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Scan.html#Scan.Pagination
38
+ // ref. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.Pagination.html
39
+ @Config("exclusive_start_key")
40
+ @ConfigDefault("{}")
41
+ def getExclusiveStartKey: JMap[String, DynamodbAttributeValue.Task]
42
+
43
+ // ref. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.ExpressionAttributeNames.html
44
+ @Config("expression_attribute_names")
45
+ @ConfigDefault("{}")
46
+ def getExpressionAttributeNames: JMap[String, String]
47
+
48
+ // ref. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.ExpressionAttributeValues.html
49
+ @Config("expression_attribute_values")
50
+ @ConfigDefault("{}")
51
+ def getExpressionAttributeValues: JMap[String, DynamodbAttributeValue.Task]
52
+
53
+ // ref. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html#Query.FilterExpression
54
+ // ref. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Scan.html#Scan.FilterExpression
55
+ @Config("filter_expression")
56
+ @ConfigDefault("null")
57
+ def getFilterExpression: Optional[String]
58
+
59
+ // ref. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/SecondaryIndexes.html
60
+ @Config("index_name")
61
+ @ConfigDefault("null")
62
+ def getIndexName: Optional[String]
63
+
64
+ // NOTE: Use batch_size for Query/Scan limit per 1 Query/Scan.
65
+ // ref. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html#Query.Limit
66
+ // ref. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Scan.html#Scan.Limit
67
+ @Config("batch_size")
68
+ @ConfigDefault("null")
69
+ def getBatchSize: Optional[Int]
70
+
71
+ // NOTE: This limit is total records limit, not the limit of Query/Scan request.
72
+ @Config("limit")
73
+ @ConfigDefault("null")
74
+ def getLimit: Optional[JLong]
75
+
76
+ // ref. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.ProjectionExpressions.html
77
+ @Config("projection_expression")
78
+ @ConfigDefault("null")
79
+ def getProjectionExpression: Optional[String]
80
+
81
+ // TODO: just reporting ?
82
+ @Config("return_consumed_capacity")
83
+ @ConfigDefault("null")
84
+ def getReturnConsumedCapacity: Optional[ReturnConsumedCapacity]
85
+
86
+ // ref. https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html#DDB-Query-request-Select
87
+ // ref. https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Scan.html#DDB-Scan-request-Select
88
+ @Config("select")
89
+ @ConfigDefault("null")
90
+ def getSelect: Optional[Select]
91
+
92
+ def getTableName: String
93
+ def setTableName(tableName: String): Unit
94
+ }
95
+
96
+ type RequestBuilderMethods = {
97
+ def setConsistentRead(v: JBoolean): Unit
98
+ def setExclusiveStartKey(v: JMap[JString, AttributeValue]): Unit
99
+ def setExpressionAttributeNames(v: JMap[JString, JString]): Unit
100
+ def setExpressionAttributeValues(v: JMap[JString, AttributeValue]): Unit
101
+ def setFilterExpression(v: JString): Unit
102
+ def setIndexName(v: JString): Unit
103
+ def setLimit(v: JInteger): Unit
104
+ def setProjectionExpression(v: JString): Unit
105
+ def setReturnConsumedCapacity(v: ReturnConsumedCapacity): Unit
106
+ def setSelect(v: Select): Unit
107
+ def setTableName(v: JString): Unit
108
+ }
109
+
110
+ }
111
+
112
+ abstract class AbstractDynamodbOperation(
113
+ task: AbstractDynamodbOperation.Task
114
+ ) extends EmbulkDynamodbOperation {
115
+
116
+ protected def calculateLoadableRecords(loadedRecords: Long): Option[Long] = {
117
+ if (!task.getLimit.isPresent) return None
118
+ val loadableRecords = task.getLimit.get() - loadedRecords
119
+ if (loadableRecords <= 0) Option(0L)
120
+ else Option(loadableRecords)
121
+ }
122
+
123
+ protected def configureRequest[
124
+ A <: AbstractDynamodbOperation.RequestBuilderMethods
125
+ ](
126
+ req: A,
127
+ lastEvaluatedKey: Option[Map[String, AttributeValue]]
128
+ ): Unit = {
129
+ def attributeValueTaskToAttributeValue(
130
+ x: (String, DynamodbAttributeValue.Task)
131
+ ): (String, AttributeValue) = {
132
+ (x._1, DynamodbAttributeValue(x._2).getOriginal)
133
+ }
134
+
135
+ req.setConsistentRead(task.getConsistentRead)
136
+ lastEvaluatedKey match {
137
+ case Some(v) => req.setExclusiveStartKey(v.asJava)
138
+ case None =>
139
+ if (!task.getExclusiveStartKey.isEmpty)
140
+ req.setExclusiveStartKey(
141
+ task.getExclusiveStartKey.asScala
142
+ .map(attributeValueTaskToAttributeValue)
143
+ .asJava
144
+ )
145
+ }
146
+
147
+ if (!task.getExpressionAttributeNames.isEmpty)
148
+ req.setExpressionAttributeNames(task.getExpressionAttributeNames)
149
+ if (!task.getExpressionAttributeValues.isEmpty)
150
+ req.setExpressionAttributeValues(
151
+ task.getExpressionAttributeValues.asScala
152
+ .map(attributeValueTaskToAttributeValue)
153
+ .asJava
154
+ )
155
+ task.getFilterExpression.ifPresent(req.setFilterExpression)
156
+ task.getIndexName.ifPresent(req.setIndexName)
157
+ task.getBatchSize.ifPresent { v =>
158
+ if (v <= 0)
159
+ throw new ConfigException(
160
+ "\"batch_size\" must be greater than or equal to 1."
161
+ )
162
+ req.setLimit(JInteger.valueOf(v)) // Note: Use BatchSize for the limit per a request.
163
+ }
164
+ task.getProjectionExpression.ifPresent(req.setProjectionExpression)
165
+ task.getReturnConsumedCapacity.ifPresent(req.setReturnConsumedCapacity)
166
+ task.getSelect.ifPresent(req.setSelect)
167
+ req.setTableName(task.getTableName)
168
+ }
169
+ }