embulk-input-dynamodb 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ }