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
@@ -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
+ }