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
@@ -1,28 +1,35 @@
1
- package org.embulk.input.dynamodb.ope
1
+ package org.embulk.input.dynamodb.deprecated.ope
2
2
 
3
3
  import java.util.{List => JList, Map => JMap}
4
4
 
5
- import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient
6
- import com.amazonaws.services.dynamodbv2.model.{AttributeValue, Condition, ScanRequest, ScanResult}
5
+ import com.amazonaws.services.dynamodbv2.{AmazonDynamoDB, AmazonDynamoDBClient}
6
+ import com.amazonaws.services.dynamodbv2.model.{
7
+ AttributeValue,
8
+ Condition,
9
+ ScanRequest,
10
+ ScanResult
11
+ }
7
12
  import org.embulk.input.dynamodb.PluginTask
8
13
  import org.embulk.spi.{BufferAllocator, PageBuilder, PageOutput, Schema}
9
14
 
10
- import scala.collection.JavaConverters._
15
+ import scala.jdk.CollectionConverters._
16
+
17
+ class ScanOperation(client: AmazonDynamoDB) extends AbstractOperation {
11
18
 
12
- class ScanOperation(client: AmazonDynamoDBClient) extends AbstractOperation {
13
19
  override def execute(
14
- task: PluginTask,
15
- schema: Schema,
16
- output: PageOutput): Unit =
17
- {
20
+ task: PluginTask,
21
+ schema: Schema,
22
+ output: PageOutput
23
+ ): Unit = {
18
24
  val allocator: BufferAllocator = task.getBufferAllocator
19
25
  val pageBuilder: PageBuilder = new PageBuilder(allocator, schema, output)
20
26
 
21
- val attributes: JList[String] = schema.getColumns.asScala.map(_.getName).asJava
27
+ val attributes: JList[String] =
28
+ schema.getColumns.asScala.map(_.getName).asJava
22
29
  val scanFilter: JMap[String, Condition] = createFilters(task).asJava
23
30
  var evaluateKey: JMap[String, AttributeValue] = null
24
31
 
25
- val scanLimit: Long = task.getScanLimit
32
+ val scanLimit: Long = task.getScanLimit
26
33
  val recordLimit: Long = task.getRecordLimit
27
34
  var recordCount: Long = 0
28
35
 
@@ -42,9 +49,9 @@ class ScanOperation(client: AmazonDynamoDBClient) extends AbstractOperation {
42
49
  val result: ScanResult = client.scan(request)
43
50
  evaluateKey = result.getLastEvaluatedKey
44
51
 
45
- val items = result.getItems.asScala.map(_.asScala.toMap)
52
+ val items = result.getItems.asScala.map(_.asScala.toMap).toSeq
46
53
  recordCount += write(pageBuilder, schema, items)
47
- } while(evaluateKey != null && (recordLimit == 0 || recordLimit > recordCount))
54
+ } while (evaluateKey != null && (recordLimit == 0 || recordLimit > recordCount))
48
55
 
49
56
  pageBuilder.finish()
50
57
  }
@@ -0,0 +1,154 @@
1
+ package org.embulk.input.dynamodb.item
2
+
3
+ import java.nio.ByteBuffer
4
+ import java.nio.charset.StandardCharsets
5
+ import java.util.{Optional, List => JList, Map => JMap}
6
+
7
+ import com.amazonaws.services.dynamodbv2.model.AttributeValue
8
+ import org.embulk.config.{Config, ConfigDefault, Task => EmbulkTask}
9
+
10
+ import scala.jdk.CollectionConverters._
11
+ import scala.util.chaining._
12
+
13
+ /**
14
+ * TODO: I want to bind directly `org.embulk.config.Config`` to `com.amazonaws.services.dynamodbv2.model.AttributeValue`.
15
+ * Should I implement `com.amazonaws.transform.JsonUnmarshallerContext`?
16
+ **/
17
+ object DynamodbAttributeValue {
18
+
19
+ trait Task extends EmbulkTask {
20
+
21
+ @Config("S")
22
+ @ConfigDefault("null")
23
+ def getS: Optional[String]
24
+
25
+ @Config("N")
26
+ @ConfigDefault("null")
27
+ def getN: Optional[String]
28
+
29
+ @Config("B")
30
+ @ConfigDefault("null")
31
+ def getB: Optional[String]
32
+
33
+ @Config("SS")
34
+ @ConfigDefault("null")
35
+ def getSS: Optional[JList[String]]
36
+
37
+ @Config("NS")
38
+ @ConfigDefault("null")
39
+ def getNS: Optional[JList[String]]
40
+
41
+ @Config("BS")
42
+ @ConfigDefault("null")
43
+ def getBS: Optional[JList[String]]
44
+
45
+ @Config("M")
46
+ @ConfigDefault("null")
47
+ def getM: Optional[JMap[String, DynamodbAttributeValue.Task]]
48
+
49
+ @Config("L")
50
+ @ConfigDefault("null")
51
+ def getL: Optional[JList[DynamodbAttributeValue.Task]]
52
+
53
+ @Config("NULL")
54
+ @ConfigDefault("null")
55
+ def getNULL: Optional[Boolean]
56
+
57
+ @Config("BOOL")
58
+ @ConfigDefault("null")
59
+ def getBOOL: Optional[Boolean]
60
+ }
61
+
62
+ def apply(task: Task): DynamodbAttributeValue = {
63
+ val original = new AttributeValue()
64
+ .tap(a => task.getS.ifPresent(v => a.setS(v)))
65
+ .tap(a => task.getN.ifPresent(v => a.setN(v)))
66
+ .tap { a =>
67
+ task.getB.ifPresent { v =>
68
+ a.setB(ByteBuffer.wrap(v.getBytes(StandardCharsets.UTF_8)))
69
+ }
70
+ }
71
+ .tap(a => task.getSS.ifPresent(v => a.setSS(v)))
72
+ .tap(a => task.getNS.ifPresent(v => a.setNS(v)))
73
+ .tap { a =>
74
+ task.getBS.ifPresent { v =>
75
+ a.setBS(
76
+ v.asScala
77
+ .map(e => ByteBuffer.wrap(e.getBytes(StandardCharsets.UTF_8)))
78
+ .asJava
79
+ )
80
+ }
81
+ }
82
+ .tap { a =>
83
+ task.getM.ifPresent { v =>
84
+ a.setM(v.asScala.map(x => (x._1, apply(x._2).getOriginal)).asJava)
85
+ }
86
+ }
87
+ .tap(a =>
88
+ task.getL.ifPresent(v =>
89
+ a.setL(v.asScala.map(apply).map(_.getOriginal).asJava)
90
+ )
91
+ )
92
+ .tap(a => task.getNULL.ifPresent(v => a.setNULL(v)))
93
+ .tap(a => task.getBOOL.ifPresent(v => a.setBOOL(v)))
94
+ new DynamodbAttributeValue(original)
95
+ }
96
+
97
+ def apply(original: AttributeValue): DynamodbAttributeValue = {
98
+ new DynamodbAttributeValue(original)
99
+ }
100
+
101
+ def apply(item: Map[String, AttributeValue]): DynamodbAttributeValue = {
102
+ val original = new AttributeValue().withM(item.asJava)
103
+ new DynamodbAttributeValue(original)
104
+ }
105
+ }
106
+
107
+ class DynamodbAttributeValue(original: AttributeValue) {
108
+
109
+ require(
110
+ message =
111
+ s"Invalid AttributeValue: ${original} which must have 1 attribute value.",
112
+ requirement = {
113
+ Seq(hasS, hasN, hasB, hasSS, hasNS, hasBS, hasM, hasL, hasNULL, hasBOOL)
114
+ .count(has => has) == 1
115
+ }
116
+ )
117
+
118
+ def getOriginal: AttributeValue = original
119
+ def isNull: Boolean = Option[Boolean](getOriginal.getNULL).getOrElse(false)
120
+ def hasS: Boolean = Option(getOriginal.getS).isDefined
121
+ def hasN: Boolean = Option(getOriginal.getN).isDefined
122
+ def hasB: Boolean = Option(getOriginal.getB).isDefined
123
+ def hasSS: Boolean = Option(getOriginal.getSS).isDefined
124
+ def hasNS: Boolean = Option(getOriginal.getNS).isDefined
125
+ def hasBS: Boolean = Option(getOriginal.getBS).isDefined
126
+ def hasM: Boolean = Option(getOriginal.getM).isDefined
127
+ def hasL: Boolean = Option(getOriginal.getL).isDefined
128
+ def hasNULL: Boolean = Option(getOriginal.getNULL).isDefined
129
+ def hasBOOL: Boolean = Option(getOriginal.getBOOL).isDefined
130
+ def getS: String = getOriginal.getS
131
+ def getN: String = getOriginal.getN
132
+ def getB: ByteBuffer = getOriginal.getB
133
+ def getSS: JList[String] = getOriginal.getSS
134
+ def getNS: JList[String] = getOriginal.getNS
135
+ def getBS: JList[ByteBuffer] = getOriginal.getBS
136
+ def getM: JMap[String, AttributeValue] = getOriginal.getM
137
+ def getL: JList[AttributeValue] = getOriginal.getL
138
+ def getNULL: Boolean = getOriginal.getNULL
139
+ def getBOOL: Boolean = getOriginal.getBOOL
140
+
141
+ def getType: DynamodbAttributeValueType = {
142
+ if (hasS) return DynamodbAttributeValueType.S
143
+ if (hasN) return DynamodbAttributeValueType.N
144
+ if (hasB) return DynamodbAttributeValueType.B
145
+ if (hasSS) return DynamodbAttributeValueType.SS
146
+ if (hasNS) return DynamodbAttributeValueType.NS
147
+ if (hasBS) return DynamodbAttributeValueType.BS
148
+ if (hasM) return DynamodbAttributeValueType.M
149
+ if (hasL) return DynamodbAttributeValueType.L
150
+ if (hasNULL) return DynamodbAttributeValueType.NULL
151
+ if (hasBOOL) return DynamodbAttributeValueType.BOOL
152
+ DynamodbAttributeValueType.UNKNOWN
153
+ }
154
+ }
@@ -0,0 +1,245 @@
1
+ package org.embulk.input.dynamodb.item
2
+
3
+ import java.nio.ByteBuffer
4
+ import java.nio.charset.StandardCharsets
5
+
6
+ import org.embulk.input.dynamodb.logger
7
+ import org.embulk.spi.time.{Timestamp, TimestampParser}
8
+ import org.msgpack.value.{Value, ValueFactory}
9
+
10
+ import scala.jdk.CollectionConverters._
11
+ import scala.util.chaining._
12
+
13
+ object DynamodbAttributeValueEmbulkTypeTransformable {
14
+
15
+ val TRUTHY_STRINGS: Set[String] = Set(
16
+ "true",
17
+ "True",
18
+ "TRUE",
19
+ "yes",
20
+ "Yes",
21
+ "YES",
22
+ "t",
23
+ "T",
24
+ "y",
25
+ "Y",
26
+ "on",
27
+ "On",
28
+ "ON",
29
+ "1"
30
+ )
31
+
32
+ val FALSY_STRINGS: Set[String] = Set(
33
+ "false",
34
+ "False",
35
+ "FALSE",
36
+ "no",
37
+ "No",
38
+ "NO",
39
+ "f",
40
+ "F",
41
+ "n",
42
+ "N",
43
+ "off",
44
+ "Off",
45
+ "OFF",
46
+ "0"
47
+ )
48
+ }
49
+
50
+ case class DynamodbAttributeValueEmbulkTypeTransformable(
51
+ attributeValue: DynamodbAttributeValue,
52
+ typeEnforcer: Option[DynamodbAttributeValueType] = None,
53
+ timestampParser: Option[TimestampParser] = None
54
+ ) {
55
+
56
+ private def fromAttributeValueType: DynamodbAttributeValueType =
57
+ typeEnforcer.getOrElse(attributeValue.getType)
58
+
59
+ private def convertNAsLongOrDouble(n: String): Either[Long, Double] = {
60
+ n.toLongOption match {
61
+ case Some(l) => Left(l)
62
+ case None => Right(n.toDouble)
63
+ }
64
+ }
65
+
66
+ private def convertBAsString(b: ByteBuffer): String = {
67
+ new String(b.array(), StandardCharsets.UTF_8)
68
+ }
69
+
70
+ private def hasAttributeValueType: Boolean = {
71
+ fromAttributeValueType.equals(attributeValue.getType)
72
+ }
73
+
74
+ def asMessagePack: Option[Value] = {
75
+ if (!hasAttributeValueType) return None
76
+ if (attributeValue.isNull) return None
77
+
78
+ Option(fromAttributeValueType match {
79
+ case DynamodbAttributeValueType.S =>
80
+ ValueFactory.newString(attributeValue.getS)
81
+ case DynamodbAttributeValueType.N =>
82
+ convertNAsLongOrDouble(attributeValue.getN) match {
83
+ case Left(v) => ValueFactory.newInteger(v)
84
+ case Right(v) => ValueFactory.newFloat(v)
85
+ }
86
+ case DynamodbAttributeValueType.B =>
87
+ ValueFactory.newBinary(attributeValue.getB.array())
88
+ case DynamodbAttributeValueType.SS =>
89
+ ValueFactory.newArray(
90
+ attributeValue.getSS.asScala.map(ValueFactory.newString).asJava
91
+ )
92
+ case DynamodbAttributeValueType.NS =>
93
+ ValueFactory.newArray(
94
+ attributeValue.getNS.asScala
95
+ .map(convertNAsLongOrDouble(_) match {
96
+ case Left(v) => ValueFactory.newInteger(v)
97
+ case Right(v) => ValueFactory.newFloat(v)
98
+ })
99
+ .asJava
100
+ )
101
+ case DynamodbAttributeValueType.BS =>
102
+ ValueFactory.newArray(
103
+ attributeValue.getBS.asScala
104
+ .map(b => ValueFactory.newBinary(b.array()))
105
+ .asJava
106
+ )
107
+ case DynamodbAttributeValueType.M =>
108
+ ValueFactory
109
+ .newMapBuilder()
110
+ .tap { builder =>
111
+ attributeValue.getM.asScala.foreach { x =>
112
+ builder.put(
113
+ ValueFactory.newString(x._1),
114
+ DynamodbAttributeValueEmbulkTypeTransformable(
115
+ DynamodbAttributeValue(x._2)
116
+ ).asMessagePack.getOrElse(ValueFactory.newNil())
117
+ )
118
+ }
119
+ }
120
+ .build()
121
+ case DynamodbAttributeValueType.L =>
122
+ ValueFactory.newArray(
123
+ attributeValue.getL.asScala.map { av =>
124
+ DynamodbAttributeValueEmbulkTypeTransformable(
125
+ DynamodbAttributeValue(av)
126
+ ).asMessagePack.getOrElse(ValueFactory.newNil())
127
+ }.asJava
128
+ )
129
+ case DynamodbAttributeValueType.BOOL =>
130
+ ValueFactory.newBoolean(attributeValue.getBOOL)
131
+ case _ =>
132
+ logger.warn(
133
+ s"Unsupported AttributeValue: ${attributeValue.getOriginal.toString}"
134
+ )
135
+ return None
136
+ })
137
+ }
138
+
139
+ def asBoolean: Option[Boolean] = {
140
+ if (!hasAttributeValueType) return None
141
+ if (attributeValue.isNull) return None
142
+
143
+ Option(fromAttributeValueType match {
144
+ case DynamodbAttributeValueType.S =>
145
+ if (DynamodbAttributeValueEmbulkTypeTransformable.TRUTHY_STRINGS
146
+ .contains(attributeValue.getS)) true
147
+ else if (DynamodbAttributeValueEmbulkTypeTransformable.FALSY_STRINGS
148
+ .contains(attributeValue.getS)) false
149
+ else return None
150
+ case DynamodbAttributeValueType.N =>
151
+ convertNAsLongOrDouble(attributeValue.getN) match {
152
+ case Left(v) => v > 0
153
+ case Right(v) => v > 0.0
154
+ }
155
+ case DynamodbAttributeValueType.B =>
156
+ val s = convertBAsString(attributeValue.getB)
157
+ if (DynamodbAttributeValueEmbulkTypeTransformable.TRUTHY_STRINGS
158
+ .contains(s))
159
+ true
160
+ else if (DynamodbAttributeValueEmbulkTypeTransformable.FALSY_STRINGS
161
+ .contains(s)) false
162
+ else return None
163
+ case DynamodbAttributeValueType.BOOL => attributeValue.getBOOL
164
+ case unsupported =>
165
+ logger.debug(s"cannot convert ${unsupported.toString} as boolean.")
166
+ return None
167
+ })
168
+ }
169
+
170
+ def asLong: Option[Long] = {
171
+ if (!hasAttributeValueType) return None
172
+ if (attributeValue.isNull) return None
173
+
174
+ Option(fromAttributeValueType match {
175
+ case DynamodbAttributeValueType.S =>
176
+ convertNAsLongOrDouble(attributeValue.getS) match {
177
+ case Left(v) => v
178
+ case Right(v) => v.toLong
179
+ }
180
+ case DynamodbAttributeValueType.N =>
181
+ convertNAsLongOrDouble(attributeValue.getN) match {
182
+ case Left(v) => v
183
+ case Right(v) => v.toLong
184
+ }
185
+ case DynamodbAttributeValueType.B =>
186
+ convertNAsLongOrDouble(convertBAsString(attributeValue.getB)) match {
187
+ case Left(v) => v
188
+ case Right(v) => v.toLong
189
+ }
190
+ case DynamodbAttributeValueType.BOOL =>
191
+ if (attributeValue.getBOOL) 1L
192
+ else 0L
193
+ case unsupported =>
194
+ logger.debug(s"cannot convert ${unsupported.toString} as long.")
195
+ return None
196
+ })
197
+ }
198
+
199
+ def asDouble: Option[Double] = {
200
+ if (!hasAttributeValueType) return None
201
+ if (attributeValue.isNull) return None
202
+
203
+ fromAttributeValueType match {
204
+ case DynamodbAttributeValueType.S => attributeValue.getS.toDoubleOption
205
+ case DynamodbAttributeValueType.N => attributeValue.getN.toDoubleOption
206
+ case DynamodbAttributeValueType.B =>
207
+ convertBAsString(attributeValue.getB).toDoubleOption
208
+ case DynamodbAttributeValueType.BOOL =>
209
+ Option(
210
+ if (attributeValue.getBOOL) 1.0d
211
+ else 0.0d
212
+ )
213
+ case unsupported =>
214
+ logger.debug(s"cannot convert ${unsupported.toString} as double.")
215
+ None
216
+ }
217
+ }
218
+
219
+ def asString: Option[String] = {
220
+ if (!hasAttributeValueType) return None
221
+ if (attributeValue.isNull) return None
222
+
223
+ Option(fromAttributeValueType match {
224
+ case DynamodbAttributeValueType.S => attributeValue.getS
225
+ case DynamodbAttributeValueType.N => attributeValue.getN
226
+ case DynamodbAttributeValueType.B => convertBAsString(attributeValue.getB)
227
+ case DynamodbAttributeValueType.SS => asMessagePack.map(_.toJson).get
228
+ case DynamodbAttributeValueType.NS => asMessagePack.map(_.toJson).get
229
+ case DynamodbAttributeValueType.BS => asMessagePack.map(_.toJson).get
230
+ case DynamodbAttributeValueType.M => asMessagePack.map(_.toJson).get
231
+ case DynamodbAttributeValueType.L => asMessagePack.map(_.toJson).get
232
+ case DynamodbAttributeValueType.BOOL => attributeValue.getBOOL.toString
233
+ case _ =>
234
+ logger.warn(
235
+ s"Unsupported AttributeValue: ${attributeValue.getOriginal.toString}"
236
+ )
237
+ return None
238
+ })
239
+ }
240
+
241
+ def asTimestamp: Option[Timestamp] = {
242
+ timestampParser.flatMap(p => asString.map(p.parse))
243
+ }
244
+
245
+ }