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,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
|
}
|