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.
- checksums.yaml +4 -4
- data/.github/workflows/master.yml +34 -0
- data/.github/workflows/test.yml +30 -0
- data/.scalafmt.conf +5 -0
- data/CHANGELOG.md +49 -0
- data/README.md +204 -54
- data/build.gradle +53 -44
- data/example/config-deprecated.yml +20 -0
- data/example/config-query-as-json.yml +18 -0
- data/example/config-query.yml +22 -0
- data/example/config-scan.yml +18 -0
- data/example/prepare_dynamodb_table.sh +67 -0
- data/gradle/wrapper/gradle-wrapper.jar +0 -0
- data/gradle/wrapper/gradle-wrapper.properties +1 -2
- data/gradlew +67 -48
- data/gradlew.bat +20 -10
- data/{test/run_dynamodb_local.sh → run_dynamodb_local.sh} +2 -1
- data/settings.gradle +1 -0
- data/src/main/scala/org/embulk/input/dynamodb/DeprecatedDynamodbInputPlugin.scala +73 -0
- data/src/main/scala/org/embulk/input/dynamodb/DynamodbInputPlugin.scala +76 -25
- data/src/main/scala/org/embulk/input/dynamodb/PluginTask.scala +132 -32
- data/src/main/scala/org/embulk/input/dynamodb/aws/Aws.scala +44 -0
- data/src/main/scala/org/embulk/input/dynamodb/aws/AwsClientConfiguration.scala +37 -0
- data/src/main/scala/org/embulk/input/dynamodb/aws/AwsCredentials.scala +240 -0
- data/src/main/scala/org/embulk/input/dynamodb/aws/AwsDynamodbConfiguration.scala +35 -0
- data/src/main/scala/org/embulk/input/dynamodb/aws/AwsEndpointConfiguration.scala +79 -0
- data/src/main/scala/org/embulk/input/dynamodb/aws/HttpProxy.scala +61 -0
- data/src/main/scala/org/embulk/input/dynamodb/deprecated/AttributeValueHelper.scala +72 -0
- data/src/main/scala/org/embulk/input/dynamodb/{Filter.scala → deprecated/Filter.scala} +3 -3
- data/src/main/scala/org/embulk/input/dynamodb/{FilterConfig.scala → deprecated/FilterConfig.scala} +13 -13
- data/src/main/scala/org/embulk/input/dynamodb/{ope → deprecated/ope}/AbstractOperation.scala +36 -18
- data/src/main/scala/org/embulk/input/dynamodb/{ope → deprecated/ope}/QueryOperation.scala +21 -13
- data/src/main/scala/org/embulk/input/dynamodb/{ope → deprecated/ope}/ScanOperation.scala +20 -13
- data/src/main/scala/org/embulk/input/dynamodb/item/DynamodbAttributeValue.scala +154 -0
- data/src/main/scala/org/embulk/input/dynamodb/item/DynamodbAttributeValueEmbulkTypeTransformable.scala +245 -0
- data/src/main/scala/org/embulk/input/dynamodb/item/DynamodbAttributeValueType.scala +33 -0
- data/src/main/scala/org/embulk/input/dynamodb/item/DynamodbItemColumnVisitor.scala +50 -0
- data/src/main/scala/org/embulk/input/dynamodb/item/DynamodbItemConsumer.scala +40 -0
- data/src/main/scala/org/embulk/input/dynamodb/item/DynamodbItemIterator.scala +19 -0
- data/src/main/scala/org/embulk/input/dynamodb/item/DynamodbItemReader.scala +64 -0
- data/src/main/scala/org/embulk/input/dynamodb/item/DynamodbItemSchema.scala +135 -0
- data/src/main/scala/org/embulk/input/dynamodb/operation/AbstractDynamodbOperation.scala +169 -0
- data/src/main/scala/org/embulk/input/dynamodb/operation/DynamodbOperationProxy.scala +59 -0
- data/src/main/scala/org/embulk/input/dynamodb/operation/DynamodbQueryOperation.scala +72 -0
- data/src/main/scala/org/embulk/input/dynamodb/operation/DynamodbScanOperation.scala +93 -0
- data/src/main/scala/org/embulk/input/dynamodb/operation/EmbulkDynamodbOperation.scala +15 -0
- data/src/main/scala/org/embulk/input/dynamodb/package.scala +4 -9
- data/src/test/scala/org/embulk/input/dynamodb/AttributeValueHelperTest.scala +245 -101
- data/src/test/scala/org/embulk/input/dynamodb/AwsCredentialsTest.scala +150 -97
- data/src/test/scala/org/embulk/input/dynamodb/DynamodbQueryOperationTest.scala +188 -0
- data/src/test/scala/org/embulk/input/dynamodb/DynamodbScanOperationTest.scala +181 -0
- data/src/test/scala/org/embulk/input/dynamodb/testutil/EmbulkTestBase.scala +85 -0
- metadata +73 -49
- data/circle.yml +0 -16
- data/config/checkstyle/checkstyle.xml +0 -128
- data/config/checkstyle/default.xml +0 -108
- data/src/main/scala/org/embulk/input/dynamodb/AttributeValueHelper.scala +0 -41
- data/src/main/scala/org/embulk/input/dynamodb/AwsCredentials.scala +0 -63
- data/src/main/scala/org/embulk/input/dynamodb/DynamoDBClient.scala +0 -23
- data/src/test/resources/yaml/authMethodBasic.yml +0 -21
- data/src/test/resources/yaml/authMethodBasic_Error.yml +0 -19
- data/src/test/resources/yaml/authMethodEnv.yml +0 -19
- data/src/test/resources/yaml/authMethodProfile.yml +0 -20
- data/src/test/resources/yaml/dynamodb-local-query.yml +0 -25
- data/src/test/resources/yaml/dynamodb-local-scan.yml +0 -23
- data/src/test/resources/yaml/notSetAuthMethod.yml +0 -20
- data/src/test/scala/org/embulk/input/dynamodb/ope/QueryOperationTest.scala +0 -83
- data/src/test/scala/org/embulk/input/dynamodb/ope/ScanOperationTest.scala +0 -83
- data/test/create_table.sh +0 -16
- data/test/put_items.sh +0 -25
@@ -0,0 +1,59 @@
|
|
1
|
+
package org.embulk.input.dynamodb.operation
|
2
|
+
|
3
|
+
import java.util.Optional
|
4
|
+
|
5
|
+
import com.amazonaws.services.dynamodbv2.model.AttributeValue
|
6
|
+
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB
|
7
|
+
import org.embulk.config.{
|
8
|
+
Config,
|
9
|
+
ConfigDefault,
|
10
|
+
ConfigException,
|
11
|
+
Task => EmbulkTask
|
12
|
+
}
|
13
|
+
|
14
|
+
object DynamodbOperationProxy {
|
15
|
+
|
16
|
+
trait Task extends EmbulkTask {
|
17
|
+
|
18
|
+
@Config("scan")
|
19
|
+
@ConfigDefault("null")
|
20
|
+
def getScan: Optional[DynamodbScanOperation.Task]
|
21
|
+
|
22
|
+
@Config("query")
|
23
|
+
@ConfigDefault("null")
|
24
|
+
def getQuery: Optional[DynamodbQueryOperation.Task]
|
25
|
+
|
26
|
+
@Config("table")
|
27
|
+
def getTable: String
|
28
|
+
}
|
29
|
+
|
30
|
+
def apply(task: Task): DynamodbOperationProxy = {
|
31
|
+
if (task.getScan.isPresent && task.getQuery.isPresent)
|
32
|
+
throw new ConfigException("You can use either \"scan\" or \"query\".")
|
33
|
+
if (!task.getScan.isPresent && !task.getQuery.isPresent)
|
34
|
+
throw new ConfigException("You must set either \"scan\" or \"query\".")
|
35
|
+
task.getScan.ifPresent(_.setTableName(task.getTable))
|
36
|
+
task.getQuery.ifPresent(_.setTableName(task.getTable))
|
37
|
+
new DynamodbOperationProxy(task)
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
case class DynamodbOperationProxy(task: DynamodbOperationProxy.Task)
|
42
|
+
extends EmbulkDynamodbOperation {
|
43
|
+
|
44
|
+
private def getOperation: EmbulkDynamodbOperation = {
|
45
|
+
task.getScan.ifPresent(t => return DynamodbScanOperation(t))
|
46
|
+
task.getQuery.ifPresent(t => return DynamodbQueryOperation(t))
|
47
|
+
throw new IllegalStateException()
|
48
|
+
}
|
49
|
+
|
50
|
+
private val operation: EmbulkDynamodbOperation = getOperation
|
51
|
+
|
52
|
+
override def getEmbulkTaskCount: Int = operation.getEmbulkTaskCount
|
53
|
+
|
54
|
+
override def run(
|
55
|
+
dynamodb: AmazonDynamoDB,
|
56
|
+
embulkTaskIndex: Int,
|
57
|
+
f: Seq[Map[String, AttributeValue]] => Unit
|
58
|
+
): Unit = operation.run(dynamodb, embulkTaskIndex, f)
|
59
|
+
}
|
@@ -0,0 +1,72 @@
|
|
1
|
+
package org.embulk.input.dynamodb.operation
|
2
|
+
|
3
|
+
import com.amazonaws.services.dynamodbv2.model.{AttributeValue, QueryRequest}
|
4
|
+
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB
|
5
|
+
import org.embulk.config.{Config, ConfigDefault}
|
6
|
+
import org.embulk.input.dynamodb.logger
|
7
|
+
|
8
|
+
import scala.jdk.CollectionConverters._
|
9
|
+
import scala.util.chaining._
|
10
|
+
|
11
|
+
object DynamodbQueryOperation {
|
12
|
+
|
13
|
+
trait Task extends AbstractDynamodbOperation.Task {
|
14
|
+
|
15
|
+
// ref. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html#Query.KeyConditionExpressions
|
16
|
+
@Config("key_condition_expression")
|
17
|
+
def getKeyConditionExpression: String
|
18
|
+
|
19
|
+
// TODO: Is it needed in the embulk context?
|
20
|
+
// ref. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html#Query.KeyConditionExpressions
|
21
|
+
@Config("scan_index_forward")
|
22
|
+
@ConfigDefault("true")
|
23
|
+
def getScanIndexForward: Boolean
|
24
|
+
|
25
|
+
}
|
26
|
+
}
|
27
|
+
|
28
|
+
case class DynamodbQueryOperation(task: DynamodbQueryOperation.Task)
|
29
|
+
extends AbstractDynamodbOperation(task) {
|
30
|
+
|
31
|
+
private def newRequest(
|
32
|
+
lastEvaluatedKey: Option[Map[String, AttributeValue]]
|
33
|
+
): QueryRequest = {
|
34
|
+
new QueryRequest()
|
35
|
+
.tap(configureRequest(_, lastEvaluatedKey))
|
36
|
+
.tap(r => r.setKeyConditionExpression(task.getKeyConditionExpression))
|
37
|
+
.tap(r => r.setScanIndexForward(task.getScanIndexForward))
|
38
|
+
}
|
39
|
+
|
40
|
+
private def runInternal(
|
41
|
+
dynamodb: AmazonDynamoDB,
|
42
|
+
f: Seq[Map[String, AttributeValue]] => Unit,
|
43
|
+
lastEvaluatedKey: Option[Map[String, AttributeValue]] = None,
|
44
|
+
loadedRecords: Long = 0
|
45
|
+
): Unit = {
|
46
|
+
val loadableRecords: Option[Long] = calculateLoadableRecords(loadedRecords)
|
47
|
+
|
48
|
+
val result = dynamodb.query(newRequest(lastEvaluatedKey).tap { req =>
|
49
|
+
logger.info(s"Call DynamodbQueryRequest: ${req.toString}")
|
50
|
+
})
|
51
|
+
loadableRecords match {
|
52
|
+
case Some(v) if (result.getCount > v) =>
|
53
|
+
f(result.getItems.asScala.take(v.toInt).map(_.asScala.toMap).toSeq)
|
54
|
+
case None =>
|
55
|
+
f(result.getItems.asScala.map(_.asScala.toMap).toSeq)
|
56
|
+
Option(result.getLastEvaluatedKey).foreach { lastEvaluatedKey =>
|
57
|
+
runInternal(
|
58
|
+
dynamodb,
|
59
|
+
f,
|
60
|
+
lastEvaluatedKey = Option(lastEvaluatedKey.asScala.toMap),
|
61
|
+
loadedRecords = loadedRecords + result.getCount
|
62
|
+
)
|
63
|
+
}
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
override def run(
|
68
|
+
dynamodb: AmazonDynamoDB,
|
69
|
+
embulkTaskIndex: Int,
|
70
|
+
f: Seq[Map[String, AttributeValue]] => Unit
|
71
|
+
): Unit = runInternal(dynamodb, f)
|
72
|
+
}
|
@@ -0,0 +1,93 @@
|
|
1
|
+
package org.embulk.input.dynamodb.operation
|
2
|
+
|
3
|
+
import java.util.Optional
|
4
|
+
|
5
|
+
import com.amazonaws.services.dynamodbv2.model.{AttributeValue, ScanRequest}
|
6
|
+
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB
|
7
|
+
import org.embulk.config.{Config, ConfigDefault, ConfigException}
|
8
|
+
import org.embulk.input.dynamodb.logger
|
9
|
+
|
10
|
+
import scala.jdk.CollectionConverters._
|
11
|
+
import scala.util.chaining._
|
12
|
+
|
13
|
+
object DynamodbScanOperation {
|
14
|
+
|
15
|
+
trait Task extends AbstractDynamodbOperation.Task {
|
16
|
+
|
17
|
+
// ref. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Scan.html#Scan.ParallelScan
|
18
|
+
@Config("segment")
|
19
|
+
@ConfigDefault("null")
|
20
|
+
def getSegment: Optional[Int]
|
21
|
+
|
22
|
+
// ref. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Scan.html#Scan.ParallelScan
|
23
|
+
@Config("total_segment")
|
24
|
+
@ConfigDefault("null")
|
25
|
+
def getTotalSegment: Optional[Int]
|
26
|
+
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
case class DynamodbScanOperation(task: DynamodbScanOperation.Task)
|
31
|
+
extends AbstractDynamodbOperation(task) {
|
32
|
+
|
33
|
+
override def getEmbulkTaskCount: Int = {
|
34
|
+
if (task.getTotalSegment.isPresent && task.getSegment.isPresent) 1
|
35
|
+
else if (task.getTotalSegment.isPresent && !task.getSegment.isPresent)
|
36
|
+
task.getTotalSegment.get()
|
37
|
+
else if (!task.getTotalSegment.isPresent && !task.getSegment.isPresent) 1
|
38
|
+
else // if (!task.getTotalSegment.isPresent && task.getSegment.isPresent)
|
39
|
+
throw new ConfigException(
|
40
|
+
"\"segment\" option must be set with \"total_segment\" option."
|
41
|
+
)
|
42
|
+
}
|
43
|
+
|
44
|
+
private def newRequest(
|
45
|
+
embulkTaskIndex: Int,
|
46
|
+
lastEvaluatedKey: Option[Map[String, AttributeValue]]
|
47
|
+
): ScanRequest = {
|
48
|
+
new ScanRequest()
|
49
|
+
.tap(configureRequest(_, lastEvaluatedKey))
|
50
|
+
.tap { r =>
|
51
|
+
task.getTotalSegment.ifPresent { totalSegment =>
|
52
|
+
r.setTotalSegments(totalSegment)
|
53
|
+
r.setSegment(task.getSegment.orElse(embulkTaskIndex))
|
54
|
+
}
|
55
|
+
}
|
56
|
+
}
|
57
|
+
|
58
|
+
private def runInternal(
|
59
|
+
dynamodb: AmazonDynamoDB,
|
60
|
+
embulkTaskIndex: Int,
|
61
|
+
f: Seq[Map[String, AttributeValue]] => Unit,
|
62
|
+
lastEvaluatedKey: Option[Map[String, AttributeValue]] = None,
|
63
|
+
loadedRecords: Long = 0
|
64
|
+
): Unit = {
|
65
|
+
val loadableRecords: Option[Long] = calculateLoadableRecords(loadedRecords)
|
66
|
+
|
67
|
+
val result =
|
68
|
+
dynamodb.scan(newRequest(embulkTaskIndex, lastEvaluatedKey).tap { req =>
|
69
|
+
logger.info(s"Call DynamodbQueryRequest: ${req.toString}")
|
70
|
+
})
|
71
|
+
loadableRecords match {
|
72
|
+
case Some(v) if (result.getCount > v) =>
|
73
|
+
f(result.getItems.asScala.take(v.toInt).map(_.asScala.toMap).toSeq)
|
74
|
+
case None =>
|
75
|
+
f(result.getItems.asScala.map(_.asScala.toMap).toSeq)
|
76
|
+
Option(result.getLastEvaluatedKey).foreach { lastEvaluatedKey =>
|
77
|
+
runInternal(
|
78
|
+
dynamodb,
|
79
|
+
embulkTaskIndex,
|
80
|
+
f,
|
81
|
+
lastEvaluatedKey = Option(lastEvaluatedKey.asScala.toMap),
|
82
|
+
loadedRecords = loadedRecords + result.getCount
|
83
|
+
)
|
84
|
+
}
|
85
|
+
}
|
86
|
+
}
|
87
|
+
|
88
|
+
override def run(
|
89
|
+
dynamodb: AmazonDynamoDB,
|
90
|
+
embulkTaskIndex: Int,
|
91
|
+
f: Seq[Map[String, AttributeValue]] => Unit
|
92
|
+
): Unit = runInternal(dynamodb, embulkTaskIndex, f)
|
93
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
package org.embulk.input.dynamodb.operation
|
2
|
+
|
3
|
+
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB
|
4
|
+
import com.amazonaws.services.dynamodbv2.model.AttributeValue
|
5
|
+
|
6
|
+
trait EmbulkDynamodbOperation {
|
7
|
+
|
8
|
+
def getEmbulkTaskCount: Int = 1
|
9
|
+
|
10
|
+
def run(
|
11
|
+
dynamodb: AmazonDynamoDB,
|
12
|
+
embulkTaskIndex: Int,
|
13
|
+
f: Seq[Map[String, AttributeValue]] => Unit
|
14
|
+
): Unit
|
15
|
+
}
|
@@ -1,14 +1,9 @@
|
|
1
1
|
package org.embulk.input
|
2
2
|
|
3
|
-
import
|
4
|
-
import org.embulk.config.ConfigException
|
3
|
+
import org.slf4j.{Logger, LoggerFactory}
|
5
4
|
|
6
5
|
package object dynamodb {
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
} else {
|
11
|
-
throw new ConfigException("Required option is not set: " + message)
|
12
|
-
}
|
13
|
-
}
|
6
|
+
|
7
|
+
val logger: Logger = LoggerFactory.getLogger(classOf[DynamodbInputPlugin])
|
8
|
+
|
14
9
|
}
|
@@ -1,20 +1,21 @@
|
|
1
1
|
package org.embulk.input.dynamodb
|
2
2
|
|
3
|
-
|
4
3
|
import java.io.File
|
5
4
|
import java.{util => JUtil}
|
6
5
|
|
7
6
|
import com.amazonaws.services.dynamodbv2.model.AttributeValue
|
8
7
|
import com.fasterxml.jackson.databind.ObjectMapper
|
9
|
-
import org.embulk.input.dynamodb.AttributeValueHelper._
|
8
|
+
import org.embulk.input.dynamodb.deprecated.AttributeValueHelper._
|
10
9
|
import org.hamcrest.CoreMatchers._
|
10
|
+
import org.hamcrest.MatcherAssert.assertThat
|
11
11
|
import org.junit.Assert._
|
12
12
|
import org.junit.Test
|
13
13
|
import org.msgpack.value.ValueFactory
|
14
14
|
|
15
|
-
import scala.
|
15
|
+
import scala.jdk.CollectionConverters._
|
16
16
|
|
17
17
|
class AttributeValueHelperTest {
|
18
|
+
|
18
19
|
@Test
|
19
20
|
def decodeTest(): Unit = {
|
20
21
|
val stringValue = decodeToValue(new AttributeValue().withS("STR"))
|
@@ -23,7 +24,9 @@ class AttributeValueHelperTest {
|
|
23
24
|
val intValue = decodeToValue(new AttributeValue().withN("123456789"))
|
24
25
|
assertEquals(intValue.asNumberValue().toInt, 123456789)
|
25
26
|
|
26
|
-
val doubleValue = decodeToValue(
|
27
|
+
val doubleValue = decodeToValue(
|
28
|
+
new AttributeValue().withN("-98765432.00000001")
|
29
|
+
)
|
27
30
|
assertEquals(doubleValue.asNumberValue().toDouble, -98765432.00000001, 0.0)
|
28
31
|
|
29
32
|
val trueValue = decodeToValue(new AttributeValue().withBOOL(true))
|
@@ -36,162 +39,303 @@ class AttributeValueHelperTest {
|
|
36
39
|
assertEquals(nilValue.isNilValue, true)
|
37
40
|
}
|
38
41
|
|
39
|
-
|
40
42
|
@Test
|
41
43
|
def listDecodeTest(): Unit = {
|
42
|
-
val stringListValue = decodeToValue(
|
43
|
-
new AttributeValue().
|
44
|
-
|
45
|
-
|
44
|
+
val stringListValue = decodeToValue(
|
45
|
+
new AttributeValue().withL(
|
46
|
+
new AttributeValue().withS("ValueA"),
|
47
|
+
new AttributeValue().withS("ValueB"),
|
48
|
+
new AttributeValue().withS("ValueC")
|
49
|
+
)
|
50
|
+
)
|
46
51
|
|
47
52
|
assertTrue(stringListValue.isArrayValue)
|
48
53
|
assertEquals(stringListValue.asArrayValue().size(), 3)
|
49
54
|
|
50
55
|
assertTrue(stringListValue.asArrayValue().get(0).isStringValue)
|
51
|
-
assertEquals(
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
56
|
+
assertEquals(
|
57
|
+
stringListValue.asArrayValue().get(0).asStringValue().asString(),
|
58
|
+
"ValueA"
|
59
|
+
)
|
60
|
+
assertEquals(
|
61
|
+
stringListValue.asArrayValue().get(1).asStringValue().asString(),
|
62
|
+
"ValueB"
|
63
|
+
)
|
64
|
+
assertEquals(
|
65
|
+
stringListValue.asArrayValue().get(2).asStringValue().asString(),
|
66
|
+
"ValueC"
|
67
|
+
)
|
68
|
+
|
69
|
+
val numberListValue = decodeToValue(
|
70
|
+
new AttributeValue().withL(
|
71
|
+
new AttributeValue().withN("123"),
|
72
|
+
new AttributeValue().withN("-456"),
|
73
|
+
new AttributeValue().withN("0.0000045679"),
|
74
|
+
new AttributeValue().withN("-1234567890.123")
|
75
|
+
)
|
76
|
+
)
|
61
77
|
|
62
78
|
assertTrue(numberListValue.isArrayValue)
|
63
79
|
assertEquals(numberListValue.asArrayValue().size(), 4)
|
64
80
|
|
65
81
|
assertTrue(numberListValue.asArrayValue().get(0).isIntegerValue)
|
66
|
-
assertEquals(
|
67
|
-
|
82
|
+
assertEquals(
|
83
|
+
numberListValue.asArrayValue().get(0).asNumberValue().toInt,
|
84
|
+
123
|
85
|
+
)
|
86
|
+
assertEquals(
|
87
|
+
numberListValue.asArrayValue().get(1).asNumberValue().toInt,
|
88
|
+
-456
|
89
|
+
)
|
68
90
|
|
69
91
|
assertTrue(numberListValue.asArrayValue().get(2).isFloatValue)
|
70
|
-
assertEquals(
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
92
|
+
assertEquals(
|
93
|
+
numberListValue.asArrayValue().get(2).asNumberValue().toDouble,
|
94
|
+
0.0000045679,
|
95
|
+
0.0
|
96
|
+
)
|
97
|
+
assertEquals(
|
98
|
+
numberListValue.asArrayValue().get(3).asNumberValue().toDouble,
|
99
|
+
-1234567890.123,
|
100
|
+
0.0
|
101
|
+
)
|
102
|
+
|
103
|
+
val stringSetValue = decodeToValue(
|
104
|
+
new AttributeValue().withSS(new JUtil.HashSet[String]() {
|
76
105
|
add("ValueA")
|
77
106
|
add("ValueB")
|
78
107
|
add("ValueC")
|
79
|
-
})
|
108
|
+
})
|
109
|
+
)
|
80
110
|
|
81
111
|
assertTrue(stringSetValue.isArrayValue)
|
82
112
|
assertEquals(stringSetValue.asArrayValue().size(), 3)
|
83
113
|
|
84
|
-
assertThat(
|
114
|
+
assertThat(
|
115
|
+
List("ValueA", "ValueB", "ValueC").asJava,
|
85
116
|
hasItems(
|
86
117
|
equalTo(stringSetValue.asArrayValue().get(0).asStringValue().asString),
|
87
118
|
equalTo(stringSetValue.asArrayValue().get(1).asStringValue().asString),
|
88
|
-
equalTo(stringSetValue.asArrayValue().get(2).asStringValue().asString)
|
89
|
-
|
119
|
+
equalTo(stringSetValue.asArrayValue().get(2).asStringValue().asString)
|
120
|
+
)
|
121
|
+
)
|
90
122
|
|
91
|
-
val numberSetValue = decodeToValue(
|
92
|
-
new JUtil.HashSet[String]() {
|
123
|
+
val numberSetValue = decodeToValue(
|
124
|
+
new AttributeValue().withNS(new JUtil.HashSet[String]() {
|
93
125
|
add("123")
|
94
126
|
add("-456")
|
95
127
|
add("0.0000045679")
|
96
128
|
add("-1234567890.123")
|
97
|
-
})
|
129
|
+
})
|
130
|
+
)
|
98
131
|
|
99
132
|
assertTrue(numberSetValue.isArrayValue)
|
100
133
|
assertEquals(numberSetValue.asArrayValue().size(), 4)
|
101
134
|
}
|
102
135
|
|
103
|
-
|
104
136
|
@Test
|
105
137
|
def mapDecodeTest(): Unit = {
|
106
|
-
val stringMap = decodeToValue(
|
107
|
-
new JUtil.HashMap[String, AttributeValue]() {
|
138
|
+
val stringMap = decodeToValue(
|
139
|
+
new AttributeValue().withM(new JUtil.HashMap[String, AttributeValue]() {
|
108
140
|
put("KeyA", new AttributeValue().withS("ValueA"))
|
109
141
|
put("KeyB", new AttributeValue().withS("ValueB"))
|
110
142
|
put("KeyC", new AttributeValue().withS("ValueC"))
|
111
|
-
})
|
143
|
+
})
|
144
|
+
)
|
112
145
|
|
113
146
|
assertTrue(stringMap.isMapValue)
|
114
147
|
assertEquals(stringMap.asMapValue().size(), 3)
|
115
|
-
assertEquals(
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
148
|
+
assertEquals(
|
149
|
+
stringMap
|
150
|
+
.asMapValue()
|
151
|
+
.map()
|
152
|
+
.get(ValueFactory.newString("KeyA"))
|
153
|
+
.asStringValue()
|
154
|
+
.asString(),
|
155
|
+
"ValueA"
|
156
|
+
)
|
157
|
+
assertEquals(
|
158
|
+
stringMap
|
159
|
+
.asMapValue()
|
160
|
+
.map()
|
161
|
+
.get(ValueFactory.newString("KeyB"))
|
162
|
+
.asStringValue()
|
163
|
+
.asString(),
|
164
|
+
"ValueB"
|
165
|
+
)
|
166
|
+
assertEquals(
|
167
|
+
stringMap
|
168
|
+
.asMapValue()
|
169
|
+
.map()
|
170
|
+
.get(ValueFactory.newString("KeyC"))
|
171
|
+
.asStringValue()
|
172
|
+
.asString(),
|
173
|
+
"ValueC"
|
174
|
+
)
|
175
|
+
|
176
|
+
val numberMap = decodeToValue(
|
177
|
+
new AttributeValue().withM(new JUtil.HashMap[String, AttributeValue]() {
|
122
178
|
put("KeyA", new AttributeValue().withN("123"))
|
123
179
|
put("KeyB", new AttributeValue().withN("-456"))
|
124
180
|
put("KeyC", new AttributeValue().withN("0.0000045679"))
|
125
181
|
put("KeyD", new AttributeValue().withN("-1234567890.123"))
|
126
|
-
})
|
182
|
+
})
|
183
|
+
)
|
127
184
|
|
128
185
|
assertTrue(numberMap.isMapValue)
|
129
186
|
assertEquals(numberMap.asMapValue().size(), 4)
|
130
187
|
|
131
|
-
assertTrue(
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
188
|
+
assertTrue(
|
189
|
+
numberMap
|
190
|
+
.asMapValue()
|
191
|
+
.map()
|
192
|
+
.get(ValueFactory.newString("KeyA"))
|
193
|
+
.isIntegerValue
|
194
|
+
)
|
195
|
+
assertEquals(
|
196
|
+
numberMap
|
197
|
+
.asMapValue()
|
198
|
+
.map()
|
199
|
+
.get(ValueFactory.newString("KeyA"))
|
200
|
+
.asNumberValue()
|
201
|
+
.toInt,
|
202
|
+
123
|
203
|
+
)
|
204
|
+
assertEquals(
|
205
|
+
numberMap
|
206
|
+
.asMapValue()
|
207
|
+
.map()
|
208
|
+
.get(ValueFactory.newString("KeyB"))
|
209
|
+
.asNumberValue()
|
210
|
+
.toInt,
|
211
|
+
-456
|
212
|
+
)
|
213
|
+
|
214
|
+
assertTrue(
|
215
|
+
numberMap
|
216
|
+
.asMapValue()
|
217
|
+
.map()
|
218
|
+
.get(ValueFactory.newString("KeyC"))
|
219
|
+
.isFloatValue
|
220
|
+
)
|
221
|
+
assertEquals(
|
222
|
+
numberMap
|
223
|
+
.asMapValue()
|
224
|
+
.map()
|
225
|
+
.get(ValueFactory.newString("KeyC"))
|
226
|
+
.asFloatValue()
|
227
|
+
.toDouble,
|
228
|
+
0.0000045679,
|
229
|
+
0.0
|
230
|
+
)
|
231
|
+
assertEquals(
|
232
|
+
numberMap
|
233
|
+
.asMapValue()
|
234
|
+
.map()
|
235
|
+
.get(ValueFactory.newString("KeyD"))
|
236
|
+
.asFloatValue()
|
237
|
+
.toDouble,
|
238
|
+
-1234567890.123,
|
239
|
+
0.0
|
240
|
+
)
|
138
241
|
}
|
139
242
|
|
140
|
-
def attr[A](value: A)(implicit f: A=> AttributeValue): AttributeValue =
|
141
|
-
|
142
|
-
|
143
|
-
implicit def
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
implicit def
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
implicit def
|
156
|
-
|
243
|
+
def attr[A](value: A)(implicit f: A => AttributeValue): AttributeValue =
|
244
|
+
f(value)
|
245
|
+
|
246
|
+
implicit def StringAttributeValue(value: String): AttributeValue =
|
247
|
+
new AttributeValue().withS(value)
|
248
|
+
|
249
|
+
implicit def IntegerAttributeValue(value: Int): AttributeValue =
|
250
|
+
new AttributeValue().withN(value.toString)
|
251
|
+
|
252
|
+
implicit def LongAttributeValue(value: Long): AttributeValue =
|
253
|
+
new AttributeValue().withN(value.toString)
|
254
|
+
|
255
|
+
implicit def FloatAttributeValue(value: Float): AttributeValue =
|
256
|
+
new AttributeValue().withN(value.toString)
|
257
|
+
|
258
|
+
implicit def DoubleAttributeValue(value: Double): AttributeValue =
|
259
|
+
new AttributeValue().withN(value.toString)
|
260
|
+
|
261
|
+
implicit def BooleanAttributeValue(value: Boolean): AttributeValue =
|
262
|
+
new AttributeValue().withBOOL(value)
|
263
|
+
|
264
|
+
implicit def MapAttributeValue(
|
265
|
+
value: Map[String, AttributeValue]
|
266
|
+
): AttributeValue = new AttributeValue().withM(value.asJava)
|
267
|
+
|
268
|
+
implicit def ListAttributeValue(value: List[AttributeValue]): AttributeValue =
|
269
|
+
new AttributeValue().withL(value.asJava)
|
157
270
|
|
158
271
|
@Test
|
159
272
|
def nestedDecodeTest(): Unit = {
|
160
273
|
// TODO: Json -> AttributeValue...
|
161
|
-
val testData = decodeToValue(
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
274
|
+
val testData = decodeToValue(
|
275
|
+
attr(
|
276
|
+
Map(
|
277
|
+
"_id" -> attr("56d8e1377a72374918f73bd2"),
|
278
|
+
"index" -> attr(0),
|
279
|
+
"guid" -> attr("5309640c-499a-43f6-801d-3076c810892b"),
|
280
|
+
"isActive" -> attr(true),
|
281
|
+
"age" -> attr(37),
|
282
|
+
"name" -> attr("Battle Lancaster"),
|
283
|
+
"email" -> attr("battlelancaster@zytrac.com"),
|
284
|
+
"registered" -> attr("2014-07-16T04:40:58 -09:00"),
|
285
|
+
"latitude" -> attr(45.574906),
|
286
|
+
"longitude" -> attr(36.596302),
|
287
|
+
"tags" -> attr(
|
288
|
+
List(
|
289
|
+
attr("veniam"),
|
290
|
+
attr("exercitation"),
|
291
|
+
attr("velit"),
|
292
|
+
attr("pariatur"),
|
293
|
+
attr("sit"),
|
294
|
+
attr("non"),
|
295
|
+
attr("dolore")
|
296
|
+
)
|
297
|
+
),
|
298
|
+
"friends" -> attr(
|
299
|
+
List(
|
300
|
+
attr(
|
301
|
+
Map(
|
302
|
+
"id" -> attr(0),
|
303
|
+
"name" -> attr("Mejia Montgomery"),
|
304
|
+
"tags" -> attr(
|
305
|
+
List(attr("duis"), attr("proident"), attr("et"))
|
306
|
+
)
|
307
|
+
)
|
308
|
+
),
|
309
|
+
attr(
|
310
|
+
Map(
|
311
|
+
"id" -> attr(1),
|
312
|
+
"name" -> attr("Carpenter Reed"),
|
313
|
+
"tags" -> attr(
|
314
|
+
List(attr("labore"), attr("nisi"), attr("ipsum"))
|
315
|
+
)
|
316
|
+
)
|
317
|
+
),
|
318
|
+
attr(
|
319
|
+
Map(
|
320
|
+
"id" -> attr(2),
|
321
|
+
"name" -> attr("Gamble Watts"),
|
322
|
+
"tags" -> attr(
|
323
|
+
List(attr("occaecat"), attr("voluptate"), attr("eu"))
|
324
|
+
)
|
325
|
+
)
|
326
|
+
)
|
327
|
+
)
|
328
|
+
)
|
329
|
+
)
|
188
330
|
)
|
189
|
-
)
|
331
|
+
)
|
190
332
|
|
191
|
-
val testA = new ObjectMapper()
|
192
|
-
testData.toJson, classOf[JUtil.Map[String, Any]])
|
333
|
+
val testA = new ObjectMapper()
|
334
|
+
.readValue(testData.toJson, classOf[JUtil.Map[String, Any]])
|
193
335
|
val testB = new ObjectMapper().readValue(
|
194
|
-
new File("src/test/resources/json/test.json"),
|
336
|
+
new File("src/test/resources/json/test.json"),
|
337
|
+
classOf[JUtil.Map[String, Any]]
|
338
|
+
)
|
195
339
|
|
196
340
|
assertThat(testA, is(testB))
|
197
341
|
}
|