embulk-input-dynamodb 0.0.3 → 0.1.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 (26) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +19 -3
  3. data/build.gradle +29 -5
  4. data/circle.yml +8 -0
  5. data/config/checkstyle/checkstyle.xml +128 -0
  6. data/config/checkstyle/default.xml +108 -0
  7. data/gradle/wrapper/gradle-wrapper.jar +0 -0
  8. data/gradle/wrapper/gradle-wrapper.properties +2 -2
  9. data/lib/embulk/input/dynamodb.rb +1 -1
  10. data/src/main/scala/org/embulk/input/dynamodb/AttributeValueHelper.scala +41 -0
  11. data/src/main/scala/org/embulk/input/dynamodb/AwsCredentials.scala +71 -0
  12. data/src/main/scala/org/embulk/input/{DynamoDBUtil.scala → dynamodb/DynamoDBUtil.scala} +54 -62
  13. data/src/main/scala/org/embulk/input/{DynamodbInputPlugin.scala → dynamodb/DynamodbInputPlugin.scala} +14 -8
  14. data/src/main/scala/org/embulk/input/{Filter.scala → dynamodb/Filter.scala} +2 -2
  15. data/src/main/scala/org/embulk/input/{FilterConfig.scala → dynamodb/FilterConfig.scala} +1 -1
  16. data/src/main/scala/org/embulk/input/{PluginTask.scala → dynamodb/PluginTask.scala} +10 -2
  17. data/src/test/resources/json/test.json +50 -0
  18. data/src/test/resources/json/test.template +27 -0
  19. data/src/test/resources/yaml/authMethodBasic.yml +20 -0
  20. data/src/test/resources/yaml/authMethodBasic_Error.yml +18 -0
  21. data/src/test/resources/yaml/authMethodEnv.yml +18 -0
  22. data/src/test/resources/yaml/authMethodProfile.yml +19 -0
  23. data/src/test/resources/yaml/notSetAuthMethod.yml +19 -0
  24. data/src/test/scala/org/embulk/input/dynamodb/AttributeValueHelperTest.scala +192 -0
  25. data/src/test/scala/org/embulk/input/dynamodb/AwsCredentialsTest.scala +135 -0
  26. metadata +29 -16
@@ -1,67 +1,34 @@
1
- package org.embulk.input
1
+ package org.embulk.input.dynamodb
2
2
 
3
3
  import java.util.{ArrayList => JArrayList, List => JList, Map => JMap}
4
4
 
5
- import com.amazonaws.auth.profile.ProfileCredentialsProvider
6
- import com.amazonaws.auth.{AWSCredentials, AWSCredentialsProvider, BasicAWSCredentials}
5
+ import com.amazonaws.ClientConfiguration
7
6
  import com.amazonaws.regions.Regions
8
7
  import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient
9
- import com.amazonaws.services.dynamodbv2.model._
10
- import com.amazonaws.{AmazonClientException, ClientConfiguration}
8
+ import com.amazonaws.services.dynamodbv2.model.{AttributeValue, Condition, ScanRequest, ScanResult}
11
9
  import org.embulk.spi._
10
+ import org.embulk.spi.`type`.Types
11
+ import org.msgpack.value.{ValueFactory, Value}
12
12
 
13
13
  import scala.collection.JavaConversions._
14
+ import scala.collection.JavaConverters._
14
15
 
15
16
  object DynamoDBUtil {
16
17
  def createClient(task: PluginTask): AmazonDynamoDBClient = {
17
- try {
18
- createClientUsingIAMRole(task)
19
- } catch {
20
- case e: AmazonClientException =>
21
- createClientUsingCredentials(task)
22
- }
23
- }
24
-
25
- private def getCredentialsProvider(task: PluginTask): AWSCredentialsProvider = {
26
- {for {
27
- accessKey <- Option(task.getAccessKey.orNull)
28
- secretKey <- Option(task.getSecretKey.orNull)
29
- } yield {
30
- new AWSCredentialsProvider {
31
- override def refresh(): Unit = { }
32
- override def getCredentials: AWSCredentials = {
33
- new BasicAWSCredentials(accessKey, secretKey)
34
- }
35
- }
36
- }}.getOrElse {
37
- new ProfileCredentialsProvider()
38
- }
39
- }
40
-
41
- private def createClientUsingIAMRole(task: PluginTask): AmazonDynamoDBClient = {
42
- val client: AmazonDynamoDBClient = new AmazonDynamoDBClient(
43
- new ClientConfiguration().withMaxConnections(10))
44
- .withRegion(Regions.fromName(task.getRegion))
45
-
46
- client.describeTable(task.getTable) // FIXME
47
-
48
- client
49
- }
50
-
51
- private def createClientUsingCredentials(task: PluginTask): AmazonDynamoDBClient = {
52
- val credentialsProvider: AWSCredentialsProvider = getCredentialsProvider(task)
53
- val client: AmazonDynamoDBClient = new AmazonDynamoDBClient(
54
- credentialsProvider,
55
- new ClientConfiguration().withMaxConnections(10))
18
+ new AmazonDynamoDBClient(
19
+ AwsCredentials.getCredentialsProvider(task),
20
+ new ClientConfiguration()
21
+ .withMaxConnections(50)) // SDK Default Value
56
22
  .withRegion(Regions.fromName(task.getRegion))
57
-
58
- client.describeTable(task.getTable) // FIXME
59
-
60
- client
61
23
  }
62
24
 
63
25
 
64
- def scan(client: AmazonDynamoDBClient, task: PluginTask, schema: Schema, output: PageOutput): Unit = {
26
+ def scan(
27
+ task: PluginTask,
28
+ schema: Schema,
29
+ output: PageOutput)
30
+ (implicit client: AmazonDynamoDBClient): Unit =
31
+ {
65
32
  val allocator: BufferAllocator = task.getBufferAllocator
66
33
  val pageBuilder: PageBuilder = new PageBuilder(allocator, schema, output)
67
34
 
@@ -70,7 +37,7 @@ object DynamoDBUtil {
70
37
  schema.getColumns.foreach { column =>
71
38
  attributes.add(column.getName)
72
39
  }
73
- val scanFilter: Map[String, Condition] = createScanFilter(task)
40
+ val scanFilter: JMap[String, Condition] = createScanFilter(task)
74
41
  var evaluateKey: JMap[String, AttributeValue] = null
75
42
 
76
43
  val scanLimit: Long = task.getScanLimit
@@ -95,23 +62,24 @@ object DynamoDBUtil {
95
62
 
96
63
  result.getItems.foreach { item =>
97
64
  schema.getColumns.foreach { column =>
98
- val value = item.get(column.getName)
99
- column.getType.getName match {
100
- case "string" =>
101
- pageBuilder.setString(column, Option(value) map { _.getS } getOrElse { "" })
102
- case "long" =>
103
- pageBuilder.setLong(column, Option(value) map { _.getN.toLong } getOrElse { 0L })
104
- case "double" =>
105
- pageBuilder.setDouble(column, Option(value) map { _.getN.toDouble } getOrElse { 0D })
106
- case "boolean" =>
107
- pageBuilder.setBoolean(column, Option(value) map { _.getBOOL == true } getOrElse { false })
65
+ val value = item.asScala.get(column.getName)
66
+ column.getType match {
67
+ case Types.STRING =>
68
+ convert(column, value, pageBuilder.setString)
69
+ case Types.LONG =>
70
+ convert(column, value, pageBuilder.setLong)
71
+ case Types.DOUBLE =>
72
+ convert(column, value, pageBuilder.setDouble)
73
+ case Types.BOOLEAN =>
74
+ convert(column, value, pageBuilder.setBoolean)
75
+ case Types.JSON =>
76
+ convert(column, value, pageBuilder.setJson)
108
77
  case _ => /* Do nothing */
109
78
  }
110
79
  }
111
80
  pageBuilder.addRecord()
112
81
  recordCount += 1
113
82
  }
114
- println(s"$recordLimit $recordLimit $recordCount")
115
83
  } while(evaluateKey != null && (recordLimit == 0 || recordLimit > recordCount))
116
84
 
117
85
  pageBuilder.finish()
@@ -132,7 +100,8 @@ object DynamoDBUtil {
132
100
  filters.getFilters.map { filter =>
133
101
  val attributeValueList = collection.mutable.ArrayBuffer[AttributeValue]()
134
102
  attributeValueList += createAttributeValue(filter.getType, filter.getValue)
135
- Option(filter.getValue2).map { value2 => attributeValueList += createAttributeValue(filter.getType, value2) }
103
+ Option(filter.getValue2).map { value2 =>
104
+ attributeValueList+= createAttributeValue(filter.getType, value2) }
136
105
 
137
106
  filterMap += filter.getName -> new Condition()
138
107
  .withComparisonOperator(filter.getCondition)
@@ -153,4 +122,27 @@ object DynamoDBUtil {
153
122
  new AttributeValue().withBOOL(v.toBoolean)
154
123
  }
155
124
  }
125
+
126
+ private def convert[A](column: Column,
127
+ value: Option[AttributeValue],
128
+ f: (Column, A) => Unit)(implicit f1: Option[AttributeValue] => A): Unit =
129
+ f(column, f1(value))
130
+
131
+ implicit private def StringConvert(value: Option[AttributeValue]): String =
132
+ value.map(_.getS).getOrElse("")
133
+
134
+ implicit private def LongConvert(value: Option[AttributeValue]): Long =
135
+ value.map(_.getN.toLong).getOrElse(0L)
136
+
137
+ implicit private def DoubleConvert(value: Option[AttributeValue]): Double =
138
+ value.map(_.getN.toDouble).getOrElse(0D)
139
+
140
+ implicit private def BooleanConvert(value: Option[AttributeValue]): Boolean =
141
+ value.exists(_.getS.toBoolean)
142
+
143
+ implicit private def JsonConvert(value: Option[AttributeValue]): Value = {
144
+ value.map { attr =>
145
+ AttributeValueHelper.decodeToValue(attr)
146
+ }.getOrElse(ValueFactory.newNil())
147
+ }
156
148
  }
@@ -1,4 +1,4 @@
1
- package org.embulk.input
1
+ package org.embulk.input.dynamodb
2
2
 
3
3
  import java.util.{List => JList}
4
4
 
@@ -21,15 +21,21 @@ class DynamodbInputPlugin extends InputPlugin {
21
21
  Exec.newConfigDiff()
22
22
  }
23
23
 
24
- def cleanup(taskSource: TaskSource, schema: Schema, taskCount: Int, successCommitReports: JList[CommitReport]): Unit = {
25
- }
26
-
27
- def run(taskSource: TaskSource, schema: Schema, taskIndex: Int, output: PageOutput): CommitReport = {
24
+ def run(taskSource: TaskSource, schema: Schema, taskIndex: Int, output: PageOutput): TaskReport = {
28
25
  val task: PluginTask = taskSource.loadTask(classOf[PluginTask])
29
26
 
30
- val client: AmazonDynamoDBClient = DynamoDBUtil.createClient(task)
31
- DynamoDBUtil.scan(client, task, schema, output)
27
+ implicit val client: AmazonDynamoDBClient = DynamoDBUtil.createClient(task)
28
+ DynamoDBUtil.scan(task, schema, output)
29
+
30
+ Exec.newTaskReport()
31
+ }
32
+
33
+ def cleanup(taskSource: TaskSource, schema: Schema, taskCount: Int, successTaskReports: JList[TaskReport]): Unit = {
34
+ // TODO
35
+ }
32
36
 
33
- Exec.newCommitReport()
37
+ def guess(config: ConfigSource): ConfigDiff = {
38
+ // TODO
39
+ null
34
40
  }
35
41
  }
@@ -1,8 +1,8 @@
1
- package org.embulk.input
1
+ package org.embulk.input.dynamodb
2
2
 
3
3
  import java.util.{List => JList}
4
4
 
5
- import com.fasterxml.jackson.annotation.{JsonValue, JsonCreator}
5
+ import com.fasterxml.jackson.annotation.{JsonCreator, JsonValue}
6
6
  import com.google.common.base.Objects
7
7
 
8
8
  class Filter {
@@ -1,4 +1,4 @@
1
- package org.embulk.input
1
+ package org.embulk.input.dynamodb
2
2
 
3
3
  import com.fasterxml.jackson.annotation.JsonProperty
4
4
  import com.google.common.base.Objects
@@ -1,10 +1,14 @@
1
- package org.embulk.input
1
+ package org.embulk.input.dynamodb
2
2
 
3
3
  import com.google.common.base.Optional
4
- import org.embulk.config.{ConfigInject, ConfigDefault, Config, Task}
4
+ import org.embulk.config.{Config, ConfigDefault, ConfigInject, Task}
5
5
  import org.embulk.spi.{BufferAllocator, SchemaConfig}
6
6
 
7
7
  trait PluginTask extends Task {
8
+ @Config("auth_method")
9
+ @ConfigDefault("null")
10
+ def getAuthMethod: Optional[String]
11
+
8
12
  @Config("access_key")
9
13
  @ConfigDefault("null")
10
14
  def getAccessKey: Optional[String]
@@ -13,6 +17,10 @@ trait PluginTask extends Task {
13
17
  @ConfigDefault("null")
14
18
  def getSecretKey: Optional[String]
15
19
 
20
+ @Config("profile_name")
21
+ @ConfigDefault("null")
22
+ def getProfileName: Optional[String]
23
+
16
24
  @Config("region")
17
25
  @ConfigDefault("ap-northeast-1")
18
26
  def getRegion: String
@@ -0,0 +1,50 @@
1
+ {
2
+ "_id": "56d8e1377a72374918f73bd2",
3
+ "index": 0,
4
+ "guid": "5309640c-499a-43f6-801d-3076c810892b",
5
+ "isActive": true,
6
+ "age": 37,
7
+ "name": "Battle Lancaster",
8
+ "email": "battlelancaster@zytrac.com",
9
+ "registered": "2014-07-16T04:40:58 -09:00",
10
+ "latitude": 45.574906,
11
+ "longitude": 36.596302,
12
+ "tags": [
13
+ "veniam",
14
+ "exercitation",
15
+ "velit",
16
+ "pariatur",
17
+ "sit",
18
+ "non",
19
+ "dolore"
20
+ ],
21
+ "friends": [
22
+ {
23
+ "id": 0,
24
+ "name": "Mejia Montgomery",
25
+ "tags": [
26
+ "duis",
27
+ "proident",
28
+ "et"
29
+ ]
30
+ },
31
+ {
32
+ "id": 1,
33
+ "name": "Carpenter Reed",
34
+ "tags": [
35
+ "labore",
36
+ "nisi",
37
+ "ipsum"
38
+ ]
39
+ },
40
+ {
41
+ "id": 2,
42
+ "name": "Gamble Watts",
43
+ "tags": [
44
+ "occaecat",
45
+ "voluptate",
46
+ "eu"
47
+ ]
48
+ }
49
+ ]
50
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ _id: '{{objectId()}}',
3
+ index: '{{index()}}',
4
+ guid: '{{guid()}}',
5
+ isActive: '{{bool()}}',
6
+ age: '{{integer(20, 40)}}',
7
+ name: '{{firstName()}} {{surname()}}',
8
+ email: '{{email()}}',
9
+ registered: '{{date(new Date(2014, 0, 1), new Date(), "YYYY-MM-ddThh:mm:ss Z")}}',
10
+ latitude: '{{floating(-90.000001, 90)}}',
11
+ longitude: '{{floating(-180.000001, 180)}}',
12
+ tags: [
13
+ '{{repeat(7)}}',
14
+ '{{lorem(1, "words")}}'
15
+ ],
16
+ friends: [
17
+ '{{repeat(3)}}',
18
+ {
19
+ id: '{{index()}}',
20
+ name: '{{firstName()}} {{surname()}}',
21
+ tags: [
22
+ '{{repeat(3)}}',
23
+ '{{lorem(1, "words")}}'
24
+ ]
25
+ }
26
+ ]
27
+ }
@@ -0,0 +1,20 @@
1
+ in:
2
+ type: dynamodb
3
+ region: ENV_VAR
4
+ table: ENV_VAR
5
+ auth_method: basic
6
+ access_key: ENV_VAR
7
+ secret_key: ENV_VAR
8
+ columns:
9
+ - {name: pri-key, type: string}
10
+ - {name: sort-key, type: long}
11
+ - {name: value, type: string}
12
+
13
+ out:
14
+ type: file
15
+ path_prefix: result
16
+ file_ext: tsv
17
+ formatter:
18
+ type: csv
19
+ delimiter: "\t"
20
+ header_line: false
@@ -0,0 +1,18 @@
1
+ in:
2
+ type: dynamodb
3
+ region: ENV_VAR
4
+ table: ENV_VAR
5
+ auth_method: basic
6
+ columns:
7
+ - {name: pri-key, type: string}
8
+ - {name: sort-key, type: long}
9
+ - {name: value, type: string}
10
+
11
+ out:
12
+ type: file
13
+ path_prefix: result
14
+ file_ext: tsv
15
+ formatter:
16
+ type: csv
17
+ delimiter: "\t"
18
+ header_line: false
@@ -0,0 +1,18 @@
1
+ in:
2
+ type: dynamodb
3
+ region: ENV_VAR
4
+ table: ENV_VAR
5
+ auth_method: env
6
+ columns:
7
+ - {name: pri-key, type: string}
8
+ - {name: sort-key, type: long}
9
+ - {name: value, type: string}
10
+
11
+ out:
12
+ type: file
13
+ path_prefix: result
14
+ file_ext: tsv
15
+ formatter:
16
+ type: csv
17
+ delimiter: "\t"
18
+ header_line: false
@@ -0,0 +1,19 @@
1
+ in:
2
+ type: dynamodb
3
+ region: ENV_VAR
4
+ table: ENV_VAR
5
+ auth_method: profile
6
+ profile_name: ENV_VAR
7
+ columns:
8
+ - {name: pri-key, type: string}
9
+ - {name: sort-key, type: long}
10
+ - {name: value, type: string}
11
+
12
+ out:
13
+ type: file
14
+ path_prefix: result
15
+ file_ext: tsv
16
+ formatter:
17
+ type: csv
18
+ delimiter: "\t"
19
+ header_line: false
@@ -0,0 +1,19 @@
1
+ in:
2
+ type: dynamodb
3
+ region: ENV_VAR
4
+ table: ENV_VAR
5
+ access_key: ENV_VAR
6
+ secret_key: ENV_VAR
7
+ columns:
8
+ - {name: pri-key, type: string}
9
+ - {name: sort-key, type: long}
10
+ - {name: value, type: string}
11
+
12
+ out:
13
+ type: file
14
+ path_prefix: result
15
+ file_ext: tsv
16
+ formatter:
17
+ type: csv
18
+ delimiter: "\t"
19
+ header_line: false
@@ -0,0 +1,192 @@
1
+ package org.embulk.input.dynamodb
2
+
3
+
4
+ import java.io.File
5
+ import java.{util => JUtil}
6
+
7
+ import com.amazonaws.services.dynamodbv2.model.AttributeValue
8
+ import com.fasterxml.jackson.databind.ObjectMapper
9
+ import org.embulk.input.dynamodb.AttributeValueHelper._
10
+ import org.hamcrest.CoreMatchers._
11
+ import org.junit.Assert._
12
+ import org.junit.Test
13
+ import org.msgpack.value.ValueFactory
14
+
15
+ import scala.collection.JavaConverters._
16
+
17
+ class AttributeValueHelperTest {
18
+ @Test
19
+ def decodeTest(): Unit = {
20
+ val stringValue = decodeToValue(new AttributeValue().withS("STR"))
21
+ assertEquals(stringValue.asStringValue.asString, "STR")
22
+
23
+ val intValue = decodeToValue(new AttributeValue().withN("123456789"))
24
+ assertEquals(intValue.asNumberValue().toInt, 123456789)
25
+
26
+ val doubleValue = decodeToValue(new AttributeValue().withN("-98765432.00000001"))
27
+ assertEquals(doubleValue.asNumberValue().toDouble, -98765432.00000001, 0.0)
28
+
29
+ val trueValue = decodeToValue(new AttributeValue().withBOOL(true))
30
+ assertEquals(trueValue.asBooleanValue().getBoolean, true)
31
+
32
+ val falseValue = decodeToValue(new AttributeValue().withBOOL(false))
33
+ assertEquals(falseValue.asBooleanValue().getBoolean, false)
34
+
35
+ val nilValue = decodeToValue(new AttributeValue().withNULL(true))
36
+ assertEquals(nilValue.isNilValue, true)
37
+ }
38
+
39
+
40
+ @Test
41
+ def listDecodeTest(): Unit = {
42
+ val stringListValue = decodeToValue(new AttributeValue().withL(
43
+ new AttributeValue().withS("ValueA"),
44
+ new AttributeValue().withS("ValueB"),
45
+ new AttributeValue().withS("ValueC")))
46
+
47
+ assertTrue(stringListValue.isArrayValue)
48
+ assertEquals(stringListValue.asArrayValue().size(), 3)
49
+
50
+ assertTrue(stringListValue.asArrayValue().get(0).isStringValue)
51
+ assertEquals(stringListValue.asArrayValue().get(0).asStringValue().asString(), "ValueA")
52
+ assertEquals(stringListValue.asArrayValue().get(1).asStringValue().asString(), "ValueB")
53
+ assertEquals(stringListValue.asArrayValue().get(2).asStringValue().asString(), "ValueC")
54
+
55
+
56
+ val numberListValue = decodeToValue(new AttributeValue().withL(
57
+ new AttributeValue().withN("123"),
58
+ new AttributeValue().withN("-456"),
59
+ new AttributeValue().withN("0.0000045679"),
60
+ new AttributeValue().withN("-1234567890.123")))
61
+
62
+ assertTrue(numberListValue.isArrayValue)
63
+ assertEquals(numberListValue.asArrayValue().size(), 4)
64
+
65
+ assertTrue(numberListValue.asArrayValue().get(0).isIntegerValue)
66
+ assertEquals(numberListValue.asArrayValue().get(0).asNumberValue().toInt, 123)
67
+ assertEquals(numberListValue.asArrayValue().get(1).asNumberValue().toInt, -456)
68
+
69
+ assertTrue(numberListValue.asArrayValue().get(2).isFloatValue)
70
+ assertEquals(numberListValue.asArrayValue().get(2).asNumberValue().toDouble, 0.0000045679, 0.0)
71
+ assertEquals(numberListValue.asArrayValue().get(3).asNumberValue().toDouble, -1234567890.123, 0.0)
72
+
73
+
74
+ val stringSetValue = decodeToValue(new AttributeValue().withSS(
75
+ new JUtil.HashSet[String]() {
76
+ add("ValueA")
77
+ add("ValueB")
78
+ add("ValueC")
79
+ }))
80
+
81
+ assertTrue(stringSetValue.isArrayValue)
82
+ assertEquals(stringSetValue.asArrayValue().size(), 3)
83
+
84
+ assertThat(List("ValueA", "ValueB", "ValueC").asJava,
85
+ hasItems(
86
+ equalTo(stringSetValue.asArrayValue().get(0).asStringValue().asString),
87
+ equalTo(stringSetValue.asArrayValue().get(1).asStringValue().asString),
88
+ equalTo(stringSetValue.asArrayValue().get(2).asStringValue().asString)))
89
+
90
+
91
+ val numberSetValue = decodeToValue(new AttributeValue().withNS(
92
+ new JUtil.HashSet[String]() {
93
+ add("123")
94
+ add("-456")
95
+ add("0.0000045679")
96
+ add("-1234567890.123")
97
+ }))
98
+
99
+ assertTrue(numberSetValue.isArrayValue)
100
+ assertEquals(numberSetValue.asArrayValue().size(), 4)
101
+ }
102
+
103
+
104
+ @Test
105
+ def mapDecodeTest(): Unit = {
106
+ val stringMap = decodeToValue(new AttributeValue().withM(
107
+ new JUtil.HashMap[String, AttributeValue]() {
108
+ put("KeyA", new AttributeValue().withS("ValueA"))
109
+ put("KeyB", new AttributeValue().withS("ValueB"))
110
+ put("KeyC", new AttributeValue().withS("ValueC"))
111
+ }))
112
+
113
+ assertTrue(stringMap.isMapValue)
114
+ assertEquals(stringMap.asMapValue().size(), 3)
115
+ assertEquals(stringMap.asMapValue().map().get(ValueFactory.newString("KeyA")).asStringValue().asString(), "ValueA")
116
+ assertEquals(stringMap.asMapValue().map().get(ValueFactory.newString("KeyB")).asStringValue().asString(), "ValueB")
117
+ assertEquals(stringMap.asMapValue().map().get(ValueFactory.newString("KeyC")).asStringValue().asString(), "ValueC")
118
+
119
+
120
+ val numberMap = decodeToValue(new AttributeValue().withM(
121
+ new JUtil.HashMap[String, AttributeValue]() {
122
+ put("KeyA", new AttributeValue().withN("123"))
123
+ put("KeyB", new AttributeValue().withN("-456"))
124
+ put("KeyC", new AttributeValue().withN("0.0000045679"))
125
+ put("KeyD", new AttributeValue().withN("-1234567890.123"))
126
+ }))
127
+
128
+ assertTrue(numberMap.isMapValue)
129
+ assertEquals(numberMap.asMapValue().size(), 4)
130
+
131
+ assertTrue(numberMap.asMapValue().map().get(ValueFactory.newString("KeyA")).isIntegerValue)
132
+ assertEquals(numberMap.asMapValue().map().get(ValueFactory.newString("KeyA")).asNumberValue().toInt, 123)
133
+ assertEquals(numberMap.asMapValue().map().get(ValueFactory.newString("KeyB")).asNumberValue().toInt, -456)
134
+
135
+ assertTrue(numberMap.asMapValue().map().get(ValueFactory.newString("KeyC")).isFloatValue)
136
+ assertEquals(numberMap.asMapValue().map().get(ValueFactory.newString("KeyC")).asFloatValue().toDouble, 0.0000045679, 0.0)
137
+ assertEquals(numberMap.asMapValue().map().get(ValueFactory.newString("KeyD")).asFloatValue().toDouble, -1234567890.123, 0.0)
138
+ }
139
+
140
+ def attr[A](value: A)(implicit f: A=> AttributeValue): AttributeValue = f(value)
141
+ implicit def StringAttributeValue(value: String): AttributeValue = new AttributeValue().withS(value)
142
+ implicit def IntegerAttributeValue(value: Int): AttributeValue = new AttributeValue().withN(value.toString)
143
+ implicit def LongAttributeValue(value: Long): AttributeValue = new AttributeValue().withN(value.toString)
144
+ implicit def FloatAttributeValue(value: Float): AttributeValue = new AttributeValue().withN(value.toString)
145
+ implicit def DoubleAttributeValue(value: Double): AttributeValue = new AttributeValue().withN(value.toString)
146
+ implicit def BooleanAttributeValue(value: Boolean): AttributeValue = new AttributeValue().withBOOL(value)
147
+ implicit def MapAttributeValue(value: Map[String, AttributeValue]): AttributeValue
148
+ = new AttributeValue().withM(value.asJava)
149
+ implicit def ListAttributeValue(value: List[AttributeValue]): AttributeValue
150
+ = new AttributeValue().withL(value.asJava)
151
+
152
+ @Test
153
+ def nestedDecodeTest(): Unit = {
154
+ // TODO: Json -> AttributeValue...
155
+ val testData = decodeToValue(attr(Map(
156
+ "_id" -> attr("56d8e1377a72374918f73bd2"),
157
+ "index" -> attr(0),
158
+ "guid" -> attr("5309640c-499a-43f6-801d-3076c810892b"),
159
+ "isActive" -> attr(true),
160
+ "age" -> attr(37),
161
+ "name" -> attr("Battle Lancaster"),
162
+ "email" -> attr("battlelancaster@zytrac.com"),
163
+ "registered" -> attr("2014-07-16T04:40:58 -09:00"),
164
+ "latitude" -> attr(45.574906),
165
+ "longitude" -> attr(36.596302),
166
+ "tags" -> attr(List(
167
+ attr("veniam"),
168
+ attr("exercitation"),
169
+ attr("velit"),
170
+ attr("pariatur"),
171
+ attr("sit"),
172
+ attr("non"),
173
+ attr("dolore"))),
174
+ "friends" -> attr(List(
175
+ attr(Map("id" -> attr(0), "name" -> attr("Mejia Montgomery"),
176
+ "tags" -> attr(List(attr("duis"), attr("proident"), attr("et"))))),
177
+ attr(Map("id" -> attr(1), "name" -> attr("Carpenter Reed"),
178
+ "tags" -> attr(List(attr("labore"), attr("nisi"), attr("ipsum"))))),
179
+ attr(Map("id" -> attr(2), "name" -> attr("Gamble Watts"),
180
+ "tags" -> attr(List(attr("occaecat"), attr("voluptate"), attr("eu")))))
181
+ ))
182
+ )
183
+ ))
184
+
185
+ val testA = new ObjectMapper().readValue(
186
+ testData.toJson, classOf[JUtil.Map[String, Any]])
187
+ val testB = new ObjectMapper().readValue(
188
+ new File("src/test/resources/json/test.json"), classOf[JUtil.Map[String, Any]])
189
+
190
+ assertThat(testA, is(testB))
191
+ }
192
+ }