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.
- 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,35 @@
|
|
1
|
+
package org.embulk.input.dynamodb.aws
|
2
|
+
|
3
|
+
import java.util.Optional
|
4
|
+
|
5
|
+
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder
|
6
|
+
import org.embulk.config.{Config, ConfigDefault}
|
7
|
+
import org.embulk.input.dynamodb.aws.AwsDynamodbConfiguration.Task
|
8
|
+
|
9
|
+
object AwsDynamodbConfiguration {
|
10
|
+
|
11
|
+
trait Task {
|
12
|
+
|
13
|
+
@Config("enable_endpoint_discovery")
|
14
|
+
@ConfigDefault("null")
|
15
|
+
def getEnableEndpointDiscovery: Optional[Boolean]
|
16
|
+
|
17
|
+
}
|
18
|
+
|
19
|
+
def apply(task: Task): AwsDynamodbConfiguration = {
|
20
|
+
new AwsDynamodbConfiguration(task)
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
class AwsDynamodbConfiguration(task: Task) {
|
25
|
+
|
26
|
+
def configureAmazonDynamoDBClientBuilder(
|
27
|
+
builder: AmazonDynamoDBClientBuilder
|
28
|
+
): Unit = {
|
29
|
+
task.getEnableEndpointDiscovery.ifPresent { v =>
|
30
|
+
if (v) builder.enableEndpointDiscovery()
|
31
|
+
else builder.disableEndpointDiscovery()
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
}
|
@@ -0,0 +1,79 @@
|
|
1
|
+
package org.embulk.input.dynamodb.aws
|
2
|
+
|
3
|
+
import java.util.Optional
|
4
|
+
|
5
|
+
import com.amazonaws.client.builder.AwsClientBuilder
|
6
|
+
import com.amazonaws.client.builder.AwsClientBuilder.EndpointConfiguration
|
7
|
+
import com.amazonaws.regions.{DefaultAwsRegionProviderChain, Regions}
|
8
|
+
import org.embulk.config.{Config, ConfigDefault, ConfigException}
|
9
|
+
import org.embulk.input.dynamodb.aws.AwsEndpointConfiguration.Task
|
10
|
+
import org.embulk.input.dynamodb.logger
|
11
|
+
import zio.macros.annotation.delegate
|
12
|
+
|
13
|
+
import scala.util.Try
|
14
|
+
|
15
|
+
object AwsEndpointConfiguration {
|
16
|
+
|
17
|
+
trait Task {
|
18
|
+
|
19
|
+
@deprecated(message = "Use #getEndpoint() instead.", since = "0.3.0")
|
20
|
+
@Config("end_point")
|
21
|
+
@ConfigDefault("null")
|
22
|
+
def getEndPoint: Optional[String]
|
23
|
+
|
24
|
+
@Config("endpoint")
|
25
|
+
@ConfigDefault("null")
|
26
|
+
def getEndpoint: Optional[String]
|
27
|
+
|
28
|
+
@Config("region")
|
29
|
+
@ConfigDefault("null")
|
30
|
+
def getRegion: Optional[String]
|
31
|
+
}
|
32
|
+
|
33
|
+
def apply(task: Task): AwsEndpointConfiguration = {
|
34
|
+
new AwsEndpointConfiguration(AwsEndpointConfigurationTaskCompat(task))
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
case class AwsEndpointConfigurationTaskCompat(@delegate task: Task)
|
39
|
+
extends Task {
|
40
|
+
override def getEndPoint: Optional[String] = throw new NotImplementedError()
|
41
|
+
|
42
|
+
override def getEndpoint: Optional[String] = {
|
43
|
+
if (task.getEndpoint.isPresent && task.getEndPoint.isPresent)
|
44
|
+
throw new ConfigException(
|
45
|
+
"You cannot use both \"endpoint\" option and \"end_point\" option. Use \"endpoint\" option."
|
46
|
+
)
|
47
|
+
if (task.getEndPoint.isPresent) {
|
48
|
+
logger.warn(
|
49
|
+
"[Deprecated] \"end_point\" option is deprecated. Use \"endpoint\" option instead."
|
50
|
+
)
|
51
|
+
return task.getEndPoint
|
52
|
+
}
|
53
|
+
task.getEndpoint
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
class AwsEndpointConfiguration(task: Task) {
|
58
|
+
|
59
|
+
def configureAwsClientBuilder[S <: AwsClientBuilder[S, T], T](
|
60
|
+
builder: AwsClientBuilder[S, T]
|
61
|
+
): Unit = {
|
62
|
+
if (task.getRegion.isPresent && task.getEndpoint.isPresent) {
|
63
|
+
val ec =
|
64
|
+
new EndpointConfiguration(task.getEndpoint.get, task.getRegion.get)
|
65
|
+
builder.setEndpointConfiguration(ec)
|
66
|
+
}
|
67
|
+
else if (task.getRegion.isPresent && !task.getEndpoint.isPresent) {
|
68
|
+
builder.setRegion(task.getRegion.get)
|
69
|
+
}
|
70
|
+
else if (!task.getRegion.isPresent && task.getEndpoint.isPresent) {
|
71
|
+
val r: String = Try(new DefaultAwsRegionProviderChain().getRegion)
|
72
|
+
.getOrElse(Regions.DEFAULT_REGION.getName)
|
73
|
+
val e: String = task.getEndpoint.get
|
74
|
+
val ec = new EndpointConfiguration(e, r)
|
75
|
+
builder.setEndpointConfiguration(ec)
|
76
|
+
}
|
77
|
+
}
|
78
|
+
|
79
|
+
}
|
@@ -0,0 +1,61 @@
|
|
1
|
+
package org.embulk.input.dynamodb.aws
|
2
|
+
|
3
|
+
import java.util.Optional
|
4
|
+
|
5
|
+
import com.amazonaws.{ClientConfiguration, Protocol}
|
6
|
+
import org.embulk.config.{Config, ConfigDefault, ConfigException}
|
7
|
+
import org.embulk.input.dynamodb.aws.HttpProxy.Task
|
8
|
+
|
9
|
+
object HttpProxy {
|
10
|
+
|
11
|
+
trait Task {
|
12
|
+
|
13
|
+
@Config("host")
|
14
|
+
@ConfigDefault("null")
|
15
|
+
def getHost: Optional[String]
|
16
|
+
|
17
|
+
@Config("port")
|
18
|
+
@ConfigDefault("null")
|
19
|
+
def getPort: Optional[Int]
|
20
|
+
|
21
|
+
@Config("protocol")
|
22
|
+
@ConfigDefault("\"https\"")
|
23
|
+
def getProtocol: String
|
24
|
+
|
25
|
+
@Config("user")
|
26
|
+
@ConfigDefault("null")
|
27
|
+
def getUser: Optional[String]
|
28
|
+
|
29
|
+
@Config("password")
|
30
|
+
@ConfigDefault("null")
|
31
|
+
def getPassword: Optional[String]
|
32
|
+
|
33
|
+
}
|
34
|
+
|
35
|
+
def apply(task: Task): HttpProxy = {
|
36
|
+
new HttpProxy(task)
|
37
|
+
}
|
38
|
+
|
39
|
+
}
|
40
|
+
|
41
|
+
class HttpProxy(task: Task) {
|
42
|
+
|
43
|
+
def configureClientConfiguration(cc: ClientConfiguration): Unit = {
|
44
|
+
task.getHost.ifPresent(v => cc.setProxyHost(v))
|
45
|
+
task.getPort.ifPresent(v => cc.setProxyPort(v))
|
46
|
+
|
47
|
+
Protocol.values.find(p => p.name().equals(task.getProtocol)) match {
|
48
|
+
case Some(v) =>
|
49
|
+
cc.setProtocol(v)
|
50
|
+
case None =>
|
51
|
+
throw new ConfigException(
|
52
|
+
s"'${task.getProtocol}' is unsupported: `protocol` must be one of [${Protocol.values
|
53
|
+
.map(v => s"'$v'")
|
54
|
+
.mkString(", ")}]."
|
55
|
+
)
|
56
|
+
}
|
57
|
+
|
58
|
+
task.getUser.ifPresent(v => cc.setProxyUsername(v))
|
59
|
+
task.getPassword.ifPresent(v => cc.setProxyPassword(v))
|
60
|
+
}
|
61
|
+
}
|
@@ -0,0 +1,72 @@
|
|
1
|
+
package org.embulk.input.dynamodb.deprecated
|
2
|
+
|
3
|
+
import com.amazonaws.services.dynamodbv2.model.AttributeValue
|
4
|
+
import org.msgpack.value.{Value, ValueFactory}
|
5
|
+
|
6
|
+
import scala.util.Try
|
7
|
+
|
8
|
+
object AttributeValueHelper {
|
9
|
+
|
10
|
+
// referring aws-scala
|
11
|
+
def decodeToValue(value: AttributeValue): Value = {
|
12
|
+
import scala.jdk.CollectionConverters._
|
13
|
+
|
14
|
+
// FIXME: Need Encode?
|
15
|
+
lazy val _bin = Option(value.getB).map(v => ValueFactory.newBinary(v.array))
|
16
|
+
lazy val _bool = Option(value.getBOOL).map(v => ValueFactory.newBoolean(v))
|
17
|
+
lazy val _num = Option(value.getN).map(v =>
|
18
|
+
Try(v.toLong)
|
19
|
+
.map(ValueFactory.newInteger)
|
20
|
+
.getOrElse(ValueFactory.newFloat(v.toDouble))
|
21
|
+
)
|
22
|
+
lazy val _str = Option(value.getS).map(v => ValueFactory.newString(v))
|
23
|
+
lazy val _nil = Option(value.getNULL).map(v => ValueFactory.newNil)
|
24
|
+
|
25
|
+
lazy val _list = Option(value.getL).map(l =>
|
26
|
+
ValueFactory.newArray(l.asScala.map(v => decodeToValue(v)).asJava)
|
27
|
+
)
|
28
|
+
lazy val _ss = Option(value.getSS).map(l =>
|
29
|
+
ValueFactory.newArray(
|
30
|
+
l.asScala.map(v => ValueFactory.newString(v)).asJava
|
31
|
+
)
|
32
|
+
)
|
33
|
+
lazy val _ns = Option(value.getNS).map(l =>
|
34
|
+
ValueFactory.newArray(
|
35
|
+
l.asScala
|
36
|
+
.map(v =>
|
37
|
+
Try(v.toLong)
|
38
|
+
.map(ValueFactory.newInteger)
|
39
|
+
.getOrElse(ValueFactory.newFloat(v.toDouble))
|
40
|
+
)
|
41
|
+
.asJava
|
42
|
+
)
|
43
|
+
)
|
44
|
+
// FIXME: Need Encode?
|
45
|
+
lazy val _bs = Option(value.getBS).map(l =>
|
46
|
+
ValueFactory.newArray(
|
47
|
+
l.asScala.map(v => ValueFactory.newBinary(v.array)).asJava
|
48
|
+
)
|
49
|
+
)
|
50
|
+
lazy val _map = Option(value.getM).map(m =>
|
51
|
+
ValueFactory.newMap(
|
52
|
+
m.asScala
|
53
|
+
.map(v => ValueFactory.newString(v._1) -> decodeToValue(v._2))
|
54
|
+
.asJava
|
55
|
+
)
|
56
|
+
)
|
57
|
+
|
58
|
+
_bin
|
59
|
+
.orElse(_bool)
|
60
|
+
.orElse(_num)
|
61
|
+
.orElse(_str)
|
62
|
+
.orElse(_nil)
|
63
|
+
.orElse(_list)
|
64
|
+
.orElse(_ss)
|
65
|
+
.orElse(_ns)
|
66
|
+
.orElse(_bs)
|
67
|
+
.orElse(_map) match {
|
68
|
+
case None => ValueFactory.newNil
|
69
|
+
case Some(j) => j
|
70
|
+
}
|
71
|
+
}
|
72
|
+
}
|
@@ -1,4 +1,4 @@
|
|
1
|
-
package org.embulk.input.dynamodb
|
1
|
+
package org.embulk.input.dynamodb.deprecated
|
2
2
|
|
3
3
|
import java.util.{List => JList}
|
4
4
|
|
@@ -18,9 +18,9 @@ class Filter {
|
|
18
18
|
def getFilters: JList[FilterConfig] = filters
|
19
19
|
|
20
20
|
override def equals(obj: Any): Boolean = {
|
21
|
-
if(this == obj) return true
|
21
|
+
if (this == obj) return true
|
22
22
|
|
23
|
-
if(!obj.isInstanceOf[Filter]) return false
|
23
|
+
if (!obj.isInstanceOf[Filter]) return false
|
24
24
|
|
25
25
|
val other: Filter = obj.asInstanceOf[Filter]
|
26
26
|
Objects.equal(filters, other.filters)
|
data/src/main/scala/org/embulk/input/dynamodb/{FilterConfig.scala → deprecated/FilterConfig.scala}
RENAMED
@@ -1,4 +1,4 @@
|
|
1
|
-
package org.embulk.input.dynamodb
|
1
|
+
package org.embulk.input.dynamodb.deprecated
|
2
2
|
|
3
3
|
import com.fasterxml.jackson.annotation.JsonProperty
|
4
4
|
import com.google.common.base.Objects
|
@@ -11,12 +11,12 @@ class FilterConfig {
|
|
11
11
|
private var _value2: String = _
|
12
12
|
|
13
13
|
def this(
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
{
|
14
|
+
@JsonProperty("name") _name: String,
|
15
|
+
@JsonProperty("type") _type: String,
|
16
|
+
@JsonProperty("condition") _condition: String,
|
17
|
+
@JsonProperty("value") _value: String,
|
18
|
+
@JsonProperty("value2") _value2: String
|
19
|
+
) {
|
20
20
|
this()
|
21
21
|
this._name = _name
|
22
22
|
this._type = _type
|
@@ -41,15 +41,15 @@ class FilterConfig {
|
|
41
41
|
def getValue2 = _value2
|
42
42
|
|
43
43
|
override def equals(obj: Any): Boolean = {
|
44
|
-
if(this == obj) return true
|
44
|
+
if (this == obj) return true
|
45
45
|
|
46
|
-
if(!obj.isInstanceOf[FilterConfig]) return false
|
46
|
+
if (!obj.isInstanceOf[FilterConfig]) return false
|
47
47
|
|
48
48
|
val other: FilterConfig = obj.asInstanceOf[FilterConfig]
|
49
49
|
Objects.equal(this._name, other._name) &&
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
50
|
+
Objects.equal(this._type, other._type) &&
|
51
|
+
Objects.equal(this._condition, other._condition) &&
|
52
|
+
Objects.equal(this._value, other._value) &&
|
53
|
+
Objects.equal(this._value2, other._value2)
|
54
54
|
}
|
55
55
|
}
|
data/src/main/scala/org/embulk/input/dynamodb/{ope → deprecated/ope}/AbstractOperation.scala
RENAMED
@@ -1,12 +1,13 @@
|
|
1
|
-
package org.embulk.input.dynamodb.ope
|
1
|
+
package org.embulk.input.dynamodb.deprecated.ope
|
2
2
|
|
3
3
|
import com.amazonaws.services.dynamodbv2.model.{AttributeValue, Condition}
|
4
|
-
import org.embulk.input.dynamodb.
|
4
|
+
import org.embulk.input.dynamodb.PluginTask
|
5
|
+
import org.embulk.input.dynamodb.deprecated.AttributeValueHelper
|
5
6
|
import org.embulk.spi._
|
6
7
|
import org.embulk.spi.`type`.Types
|
7
8
|
import org.msgpack.value.{Value, ValueFactory}
|
8
9
|
|
9
|
-
import scala.
|
10
|
+
import scala.jdk.CollectionConverters._
|
10
11
|
|
11
12
|
abstract class AbstractOperation {
|
12
13
|
def execute(task: PluginTask, schema: Schema, output: PageOutput): Unit
|
@@ -14,20 +15,29 @@ abstract class AbstractOperation {
|
|
14
15
|
def getLimit(limit: Long, recordLimit: Long, recordCount: Long): Int = {
|
15
16
|
if (limit > 0 && recordLimit > 0) {
|
16
17
|
math.min(limit, recordLimit - recordCount).toInt
|
17
|
-
}
|
18
|
+
}
|
19
|
+
else if (limit > 0 || recordLimit > 0) {
|
18
20
|
math.max(limit, recordLimit).toInt
|
19
|
-
}
|
21
|
+
}
|
22
|
+
else {
|
23
|
+
0
|
24
|
+
}
|
20
25
|
}
|
21
26
|
|
22
27
|
def createFilters(task: PluginTask): Map[String, Condition] = {
|
23
28
|
val filterMap = collection.mutable.HashMap[String, Condition]()
|
24
29
|
|
25
|
-
Option(task.getFilters.
|
30
|
+
Option(task.getFilters.orElse(null)).map { filters =>
|
26
31
|
filters.getFilters.asScala.map { filter =>
|
27
|
-
val attributeValueList =
|
28
|
-
|
32
|
+
val attributeValueList =
|
33
|
+
collection.mutable.ArrayBuffer[AttributeValue]()
|
34
|
+
attributeValueList += createAttributeValue(
|
35
|
+
filter.getType,
|
36
|
+
filter.getValue
|
37
|
+
)
|
29
38
|
Option(filter.getValue2).map { value2 =>
|
30
|
-
attributeValueList+= createAttributeValue(filter.getType, value2)
|
39
|
+
attributeValueList += createAttributeValue(filter.getType, value2)
|
40
|
+
}
|
31
41
|
|
32
42
|
filterMap += filter.getName -> new Condition()
|
33
43
|
.withComparisonOperator(filter.getCondition)
|
@@ -49,7 +59,11 @@ abstract class AbstractOperation {
|
|
49
59
|
}
|
50
60
|
}
|
51
61
|
|
52
|
-
def write(
|
62
|
+
def write(
|
63
|
+
pageBuilder: PageBuilder,
|
64
|
+
schema: Schema,
|
65
|
+
items: Seq[Map[String, AttributeValue]]
|
66
|
+
): Long = {
|
53
67
|
var count = 0
|
54
68
|
|
55
69
|
items.foreach { item =>
|
@@ -76,26 +90,30 @@ abstract class AbstractOperation {
|
|
76
90
|
count
|
77
91
|
}
|
78
92
|
|
79
|
-
def convert[A](
|
80
|
-
|
81
|
-
|
93
|
+
def convert[A](
|
94
|
+
column: Column,
|
95
|
+
value: Option[AttributeValue],
|
96
|
+
f: (Column, A) => Unit
|
97
|
+
)(implicit f1: Option[AttributeValue] => A): Unit =
|
82
98
|
f(column, f1(value))
|
83
99
|
|
84
100
|
implicit def StringConvert(value: Option[AttributeValue]): String =
|
85
101
|
value.map(_.getS).getOrElse("")
|
86
102
|
|
87
103
|
implicit def LongConvert(value: Option[AttributeValue]): Long =
|
88
|
-
value.map(_.getN.toLong).getOrElse(0L)
|
104
|
+
value.map(_.getN).flatMap(Option(_)).map(_.toLong).getOrElse(0L)
|
89
105
|
|
90
106
|
implicit def DoubleConvert(value: Option[AttributeValue]): Double =
|
91
|
-
value.map(_.getN.toDouble).getOrElse(
|
107
|
+
value.map(_.getN).flatMap(Option(_)).map(_.toDouble).getOrElse(0d)
|
92
108
|
|
93
109
|
implicit def BooleanConvert(value: Option[AttributeValue]): Boolean =
|
94
110
|
value.exists(_.getBOOL)
|
95
111
|
|
96
112
|
implicit def JsonConvert(value: Option[AttributeValue]): Value = {
|
97
|
-
value
|
98
|
-
|
99
|
-
|
113
|
+
value
|
114
|
+
.map { attr =>
|
115
|
+
AttributeValueHelper.decodeToValue(attr)
|
116
|
+
}
|
117
|
+
.getOrElse(ValueFactory.newNil())
|
100
118
|
}
|
101
119
|
}
|
@@ -1,27 +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.{
|
5
|
+
import com.amazonaws.services.dynamodbv2.{AmazonDynamoDB, AmazonDynamoDBClient}
|
6
|
+
import com.amazonaws.services.dynamodbv2.model.{
|
7
|
+
AttributeValue,
|
8
|
+
Condition,
|
9
|
+
QueryRequest,
|
10
|
+
QueryResult
|
11
|
+
}
|
7
12
|
import org.embulk.input.dynamodb.PluginTask
|
8
13
|
import org.embulk.spi.{BufferAllocator, PageBuilder, PageOutput, Schema}
|
9
14
|
|
10
|
-
import scala.
|
15
|
+
import scala.jdk.CollectionConverters._
|
16
|
+
|
17
|
+
class QueryOperation(client: AmazonDynamoDB) extends AbstractOperation {
|
11
18
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
{
|
19
|
+
override def execute(
|
20
|
+
task: PluginTask,
|
21
|
+
schema: Schema,
|
22
|
+
output: PageOutput
|
23
|
+
): Unit = {
|
17
24
|
val allocator: BufferAllocator = task.getBufferAllocator
|
18
25
|
val pageBuilder: PageBuilder = new PageBuilder(allocator, schema, output)
|
19
26
|
|
20
|
-
val attributes: JList[String] =
|
27
|
+
val attributes: JList[String] =
|
28
|
+
schema.getColumns.asScala.map(_.getName).asJava
|
21
29
|
val conditions: JMap[String, Condition] = createFilters(task).asJava
|
22
30
|
var evaluateKey: JMap[String, AttributeValue] = null
|
23
31
|
|
24
|
-
val limit: Long
|
32
|
+
val limit: Long = math.max(task.getScanLimit, task.getLimit)
|
25
33
|
val recordLimit: Long = task.getRecordLimit
|
26
34
|
var recordCount: Long = 0
|
27
35
|
|
@@ -41,9 +49,9 @@ class QueryOperation(client: AmazonDynamoDBClient) extends AbstractOperation {
|
|
41
49
|
val result: QueryResult = client.query(request)
|
42
50
|
evaluateKey = result.getLastEvaluatedKey
|
43
51
|
|
44
|
-
val items = result.getItems.asScala.map(_.asScala.toMap)
|
52
|
+
val items = result.getItems.asScala.map(_.asScala.toMap).toSeq
|
45
53
|
recordCount += write(pageBuilder, schema, items)
|
46
|
-
} while(evaluateKey != null && (recordLimit == 0 || recordLimit > recordCount))
|
54
|
+
} while (evaluateKey != null && (recordLimit == 0 || recordLimit > recordCount))
|
47
55
|
|
48
56
|
pageBuilder.finish()
|
49
57
|
}
|