embulk-input-dynamodb 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +13 -4
- data/build.gradle +1 -1
- data/circle.yml +10 -2
- data/src/main/scala/org/embulk/input/dynamodb/AwsCredentials.scala +0 -8
- data/src/main/scala/org/embulk/input/dynamodb/DynamoDBUtil.scala +18 -11
- data/src/main/scala/org/embulk/input/dynamodb/PluginTask.scala +6 -2
- data/src/main/scala/org/embulk/input/dynamodb/package.scala +14 -0
- data/src/test/resources/yaml/dynamodb-local.yml +23 -0
- data/src/test/scala/org/embulk/input/dynamodb/AttributeValueHelperTest.scala +12 -6
- data/src/test/scala/org/embulk/input/dynamodb/DynamoDBUtilTest.scala +81 -0
- data/test/create_table.sh +16 -0
- data/test/put_items.sh +25 -0
- data/test/run_dynamodb_local.sh +7 -0
- metadata +19 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 603d3c6513d86bf773182a8214d70cb668fcf2a6
|
4
|
+
data.tar.gz: 60d9256bb5fac99e56f5424997037323e98f3868
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 55d4bdb5960bdf069804360dd0ca9b627bb7cfab0be6bb92c1012b14335019f3c1fd9dd31548fcf9b11b3e22d207efc35b8faf1172b7a5be531d62d39966d61e
|
7
|
+
data.tar.gz: 9ae148915f273eca767e740b0cd523f7d355b6f793644106320b592695124682769c47df41b643139d91111d9e541b65e692a32d31b9072a04ea1721b1fd5b35
|
data/README.md
CHANGED
@@ -9,7 +9,7 @@
|
|
9
9
|
|
10
10
|
|
11
11
|
## Configuration
|
12
|
-
- **auth_method**: AWS Credential Type.
|
12
|
+
- **auth_method**: AWS Credential Type.
|
13
13
|
Available values options are: `basic`, `env`, `instance`, `profile`, `properties`
|
14
14
|
- **basic**: AWS access key and secret access key
|
15
15
|
- **env**: Environment variables
|
@@ -21,13 +21,15 @@ Available values options are: `basic`, `env`, `instance`, `profile`, `properties
|
|
21
21
|
- **secret_key**: AWS secret key (string, required)
|
22
22
|
- If **auth_method** is set `profile`
|
23
23
|
- **profile_name**: The name of a local configuration profile (string, optional)
|
24
|
-
- **region**: Region Name (string,
|
24
|
+
- **region**: Region Name (string, optional)
|
25
|
+
- **end_point**: EndPoint URL (string, optional)
|
26
|
+
`end_point` has priority when `region` and `end_point` are specified.
|
25
27
|
- **table**: Table Name (string, required)
|
26
28
|
- **scan_limit**: DynamoDB 1time Scan Query size limit (Int, optional)
|
27
29
|
- **record_limit**: Max Record Search limit (Long, optional)
|
28
30
|
- **columns**: a key-value pairs where key is a column name and value is options for the column (required)
|
29
31
|
- **name**: Column name.
|
30
|
-
- **type**: Column values are converted to this embulk type.
|
32
|
+
- **type**: Column values are converted to this embulk type.
|
31
33
|
Available values options are: `boolean`, `long`, `double`, `string`, `json`
|
32
34
|
- **filters**: query filter (optional)
|
33
35
|
|
@@ -46,7 +48,7 @@ in:
|
|
46
48
|
- {name: ColumnB, type: double}
|
47
49
|
- {name: ColumnC, type: string}
|
48
50
|
- {name: ColumnD, type: boolean}
|
49
|
-
- {name: ColumnE, type: json}
|
51
|
+
- {name: ColumnE, type: json} # DynamoDB Map, List and Set Column Type are json.
|
50
52
|
filters:
|
51
53
|
- {name: ColumnA, type: long, condition: BETWEEN, value: 10000, value2: 20000}
|
52
54
|
- {name: ColumnC, type: string, condition: EQ, value: foobar}
|
@@ -55,6 +57,13 @@ out:
|
|
55
57
|
type: stdout
|
56
58
|
```
|
57
59
|
|
60
|
+
## Try
|
61
|
+
|
62
|
+
```
|
63
|
+
$ ./gradlew classpath
|
64
|
+
$ embulk preview -I lib your-sample.yml
|
65
|
+
```
|
66
|
+
|
58
67
|
## Build
|
59
68
|
|
60
69
|
```
|
data/build.gradle
CHANGED
data/circle.yml
CHANGED
@@ -1,8 +1,16 @@
|
|
1
|
+
machine:
|
2
|
+
services:
|
3
|
+
- docker
|
4
|
+
|
1
5
|
general:
|
2
6
|
artifacts:
|
3
7
|
- "~/embulk-input-dynamodb/build/reports/tests"
|
4
8
|
|
5
9
|
test:
|
10
|
+
pre:
|
11
|
+
- sh ./test/run_dynamodb_local.sh; sleep 2
|
12
|
+
- sh ./test/create_table.sh
|
13
|
+
- sh ./test/put_items.sh
|
6
14
|
post:
|
7
|
-
|
8
|
-
|
15
|
+
- mkdir -p $CIRCLE_TEST_REPORTS/junit/
|
16
|
+
- find . -type f -regex ".*/test-results/*/.*xml" -exec cp {} $CIRCLE_TEST_REPORTS/junit/ \;
|
@@ -6,20 +6,27 @@ import com.amazonaws.ClientConfiguration
|
|
6
6
|
import com.amazonaws.regions.Regions
|
7
7
|
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient
|
8
8
|
import com.amazonaws.services.dynamodbv2.model.{AttributeValue, Condition, ScanRequest, ScanResult}
|
9
|
+
import org.embulk.config.ConfigException
|
9
10
|
import org.embulk.spi._
|
10
11
|
import org.embulk.spi.`type`.Types
|
11
|
-
import org.msgpack.value.{
|
12
|
+
import org.msgpack.value.{Value, ValueFactory}
|
12
13
|
|
13
|
-
import scala.collection.JavaConversions._
|
14
14
|
import scala.collection.JavaConverters._
|
15
15
|
|
16
16
|
object DynamoDBUtil {
|
17
17
|
def createClient(task: PluginTask): AmazonDynamoDBClient = {
|
18
|
-
new AmazonDynamoDBClient(
|
18
|
+
val client = new AmazonDynamoDBClient(
|
19
19
|
AwsCredentials.getCredentialsProvider(task),
|
20
20
|
new ClientConfiguration()
|
21
21
|
.withMaxConnections(50)) // SDK Default Value
|
22
|
-
|
22
|
+
|
23
|
+
if (task.getEndPoint.isPresent) {
|
24
|
+
client.withEndpoint(task.getEndPoint.get())
|
25
|
+
} else if (task.getRegion.isPresent) {
|
26
|
+
client.withRegion(Regions.fromName(task.getRegion.get()))
|
27
|
+
} else {
|
28
|
+
throw new ConfigException("At least one of EndPoint or Region must be set")
|
29
|
+
}
|
23
30
|
}
|
24
31
|
|
25
32
|
|
@@ -34,10 +41,10 @@ object DynamoDBUtil {
|
|
34
41
|
|
35
42
|
val attributes: JList[String] = new JArrayList[String]()
|
36
43
|
|
37
|
-
schema.getColumns.foreach { column =>
|
44
|
+
schema.getColumns.asScala.foreach { column =>
|
38
45
|
attributes.add(column.getName)
|
39
46
|
}
|
40
|
-
val scanFilter: JMap[String, Condition] = createScanFilter(task)
|
47
|
+
val scanFilter: JMap[String, Condition] = createScanFilter(task).asJava
|
41
48
|
var evaluateKey: JMap[String, AttributeValue] = null
|
42
49
|
|
43
50
|
val scanLimit: Long = task.getScanLimit
|
@@ -60,8 +67,8 @@ object DynamoDBUtil {
|
|
60
67
|
val result: ScanResult = client.scan(request)
|
61
68
|
evaluateKey = result.getLastEvaluatedKey
|
62
69
|
|
63
|
-
result.getItems.foreach { item =>
|
64
|
-
schema.getColumns.foreach { column =>
|
70
|
+
result.getItems.asScala.foreach { item =>
|
71
|
+
schema.getColumns.asScala.foreach { column =>
|
65
72
|
val value = item.asScala.get(column.getName)
|
66
73
|
column.getType match {
|
67
74
|
case Types.STRING =>
|
@@ -97,7 +104,7 @@ object DynamoDBUtil {
|
|
97
104
|
val filterMap = collection.mutable.HashMap[String, Condition]()
|
98
105
|
|
99
106
|
Option(task.getFilters.orNull).map { filters =>
|
100
|
-
filters.getFilters.map { filter =>
|
107
|
+
filters.getFilters.asScala.map { filter =>
|
101
108
|
val attributeValueList = collection.mutable.ArrayBuffer[AttributeValue]()
|
102
109
|
attributeValueList += createAttributeValue(filter.getType, filter.getValue)
|
103
110
|
Option(filter.getValue2).map { value2 =>
|
@@ -105,7 +112,7 @@ object DynamoDBUtil {
|
|
105
112
|
|
106
113
|
filterMap += filter.getName -> new Condition()
|
107
114
|
.withComparisonOperator(filter.getCondition)
|
108
|
-
.withAttributeValueList(attributeValueList)
|
115
|
+
.withAttributeValueList(attributeValueList.asJava)
|
109
116
|
}
|
110
117
|
}
|
111
118
|
|
@@ -138,7 +145,7 @@ object DynamoDBUtil {
|
|
138
145
|
value.map(_.getN.toDouble).getOrElse(0D)
|
139
146
|
|
140
147
|
implicit private def BooleanConvert(value: Option[AttributeValue]): Boolean =
|
141
|
-
value.exists(_.
|
148
|
+
value.exists(_.getBOOL)
|
142
149
|
|
143
150
|
implicit private def JsonConvert(value: Option[AttributeValue]): Value = {
|
144
151
|
value.map { attr =>
|
@@ -22,8 +22,12 @@ trait PluginTask extends Task {
|
|
22
22
|
def getProfileName: Optional[String]
|
23
23
|
|
24
24
|
@Config("region")
|
25
|
-
@ConfigDefault("
|
26
|
-
def getRegion: String
|
25
|
+
@ConfigDefault("null")
|
26
|
+
def getRegion: Optional[String]
|
27
|
+
|
28
|
+
@Config("end_point")
|
29
|
+
@ConfigDefault("null")
|
30
|
+
def getEndPoint: Optional[String]
|
27
31
|
|
28
32
|
@Config("scan_limit")
|
29
33
|
@ConfigDefault("0")
|
@@ -0,0 +1,14 @@
|
|
1
|
+
package org.embulk.input
|
2
|
+
|
3
|
+
import com.google.common.base.Optional
|
4
|
+
import org.embulk.config.ConfigException
|
5
|
+
|
6
|
+
package object dynamodb {
|
7
|
+
def require[A](value: Optional[A], message: String): A = {
|
8
|
+
if (value.isPresent) {
|
9
|
+
value.get()
|
10
|
+
} else {
|
11
|
+
throw new ConfigException("Required option is not set: " + message)
|
12
|
+
}
|
13
|
+
}
|
14
|
+
}
|
@@ -0,0 +1,23 @@
|
|
1
|
+
in:
|
2
|
+
type: dynamodb
|
3
|
+
end_point: http://localhost:8000/
|
4
|
+
table: ENV_VAR
|
5
|
+
auth_method: basic
|
6
|
+
access_key: dummy
|
7
|
+
secret_key: dummy
|
8
|
+
columns:
|
9
|
+
- {name: pri-key, type: string}
|
10
|
+
- {name: sort-key, type: long}
|
11
|
+
- {name: doubleValue, type: double}
|
12
|
+
- {name: boolValue, type: boolean}
|
13
|
+
- {name: listValue, type: json}
|
14
|
+
- {name: mapValue, type: json}
|
15
|
+
|
16
|
+
out:
|
17
|
+
type: file
|
18
|
+
path_prefix: dynamodb-local-result
|
19
|
+
file_ext: tsv
|
20
|
+
formatter:
|
21
|
+
type: csv
|
22
|
+
delimiter: "\t"
|
23
|
+
header_line: false
|
@@ -138,12 +138,18 @@ class AttributeValueHelperTest {
|
|
138
138
|
}
|
139
139
|
|
140
140
|
def attr[A](value: A)(implicit f: A=> AttributeValue): AttributeValue = f(value)
|
141
|
-
implicit def StringAttributeValue(value: String): AttributeValue
|
142
|
-
|
143
|
-
implicit def
|
144
|
-
|
145
|
-
implicit def
|
146
|
-
|
141
|
+
implicit def StringAttributeValue(value: String): AttributeValue
|
142
|
+
= new AttributeValue().withS(value)
|
143
|
+
implicit def IntegerAttributeValue(value: Int): AttributeValue
|
144
|
+
= new AttributeValue().withN(value.toString)
|
145
|
+
implicit def LongAttributeValue(value: Long): AttributeValue
|
146
|
+
= new AttributeValue().withN(value.toString)
|
147
|
+
implicit def FloatAttributeValue(value: Float): AttributeValue
|
148
|
+
= new AttributeValue().withN(value.toString)
|
149
|
+
implicit def DoubleAttributeValue(value: Double): AttributeValue
|
150
|
+
= new AttributeValue().withN(value.toString)
|
151
|
+
implicit def BooleanAttributeValue(value: Boolean): AttributeValue
|
152
|
+
= new AttributeValue().withBOOL(value)
|
147
153
|
implicit def MapAttributeValue(value: Map[String, AttributeValue]): AttributeValue
|
148
154
|
= new AttributeValue().withM(value.asJava)
|
149
155
|
implicit def ListAttributeValue(value: List[AttributeValue]): AttributeValue
|
@@ -0,0 +1,81 @@
|
|
1
|
+
package org.embulk.input.dynamodb
|
2
|
+
|
3
|
+
import java.io.File
|
4
|
+
import java.nio.charset.Charset
|
5
|
+
import java.nio.file.{FileSystems, Files}
|
6
|
+
|
7
|
+
import com.fasterxml.jackson.databind.ObjectMapper
|
8
|
+
import com.google.inject.{Binder, Module}
|
9
|
+
import org.embulk.EmbulkEmbed
|
10
|
+
import org.embulk.config.ConfigSource
|
11
|
+
import org.embulk.plugin.InjectedPluginSource
|
12
|
+
import org.embulk.spi.InputPlugin
|
13
|
+
import org.hamcrest.CoreMatchers._
|
14
|
+
import org.junit.Assert._
|
15
|
+
import org.junit.{Before, Test}
|
16
|
+
|
17
|
+
class DynamoDBUtilTest {
|
18
|
+
private var embulk: EmbulkEmbed = null
|
19
|
+
|
20
|
+
private var EMBULK_DYNAMODB_TEST_TABLE: String = null
|
21
|
+
private var mapper: ObjectMapper = null
|
22
|
+
|
23
|
+
@Before
|
24
|
+
def createResources() {
|
25
|
+
// Get Environments
|
26
|
+
EMBULK_DYNAMODB_TEST_TABLE = System.getenv("EMBULK_DYNAMODB_TEST_TABLE")
|
27
|
+
|
28
|
+
val bootstrap = new EmbulkEmbed.Bootstrap()
|
29
|
+
bootstrap.addModules(new Module {
|
30
|
+
def configure(binder: Binder): Unit = {
|
31
|
+
InjectedPluginSource.registerPluginTo(binder,
|
32
|
+
classOf[InputPlugin],
|
33
|
+
"dynamodb",
|
34
|
+
classOf[DynamodbInputPlugin])
|
35
|
+
}
|
36
|
+
})
|
37
|
+
|
38
|
+
embulk = bootstrap.initializeCloseable()
|
39
|
+
|
40
|
+
mapper = new ObjectMapper()
|
41
|
+
}
|
42
|
+
|
43
|
+
|
44
|
+
def doTest(config: ConfigSource) {
|
45
|
+
embulk.run(config)
|
46
|
+
|
47
|
+
val fs = FileSystems.getDefault
|
48
|
+
val lines = Files.readAllLines(fs.getPath("dynamodb-local-result000.00.tsv"), Charset.forName("UTF-8"))
|
49
|
+
assertEquals(lines.size, 1)
|
50
|
+
|
51
|
+
val head = lines.get(0)
|
52
|
+
val values = head.split("\t")
|
53
|
+
|
54
|
+
assertThat(values(0), is("key-1"))
|
55
|
+
assertThat(values(1), is("0"))
|
56
|
+
assertThat(values(2), is("42.195"))
|
57
|
+
assertThat(values(3), is("true"))
|
58
|
+
|
59
|
+
val listValue = mapper.readValue(values(4).replaceAll("\"(?!\")", ""), classOf[java.util.List[Object]])
|
60
|
+
assertThat(listValue.size(), is(2))
|
61
|
+
assertThat(listValue.get(0).asInstanceOf[String], is("list-value"))
|
62
|
+
assertThat(listValue.get(1).asInstanceOf[Int], is(123))
|
63
|
+
|
64
|
+
val mapValue = mapper.readValue(values(5).replaceAll("\"(?!\")", ""), classOf[java.util.Map[String, Object]])
|
65
|
+
assert(mapValue.containsKey("map-key-1"))
|
66
|
+
assertThat(mapValue.get("map-key-1").asInstanceOf[String], is("map-value-1"))
|
67
|
+
assert(mapValue.containsKey("map-key-2"))
|
68
|
+
assertThat(mapValue.get("map-key-2").asInstanceOf[Int], is(456))
|
69
|
+
}
|
70
|
+
|
71
|
+
@Test
|
72
|
+
def scanTest() {
|
73
|
+
val config = embulk.newConfigLoader().fromYamlFile(
|
74
|
+
new File("src/test/resources/yaml/dynamodb-local.yml"))
|
75
|
+
|
76
|
+
config.getNested("in")
|
77
|
+
.set("table", EMBULK_DYNAMODB_TEST_TABLE)
|
78
|
+
|
79
|
+
doTest(config)
|
80
|
+
}
|
81
|
+
}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
|
3
|
+
aws dynamodb create-table \
|
4
|
+
--table-name='EMBULK_DYNAMODB_TEST_TABLE' \
|
5
|
+
--attribute-definitions='[
|
6
|
+
{"AttributeName":"pri-key","AttributeType":"S"},
|
7
|
+
{"AttributeName":"sort-key","AttributeType":"N"}
|
8
|
+
]' \
|
9
|
+
--key-schema='[
|
10
|
+
{"AttributeName":"pri-key","KeyType":"HASH"},
|
11
|
+
{"AttributeName":"sort-key","KeyType":"RANGE"}
|
12
|
+
]' \
|
13
|
+
--provisioned-throughput='{"ReadCapacityUnits":5, "WriteCapacityUnits":5}' \
|
14
|
+
--endpoint-url http://localhost:8000 \
|
15
|
+
--region us-east-1
|
16
|
+
|
data/test/put_items.sh
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
|
3
|
+
aws dynamodb put-item \
|
4
|
+
--table-name='EMBULK_DYNAMODB_TEST_TABLE' \
|
5
|
+
--item='{
|
6
|
+
"pri-key" : { "S" : "key-1" },
|
7
|
+
"sort-key" : { "N" : "0" },
|
8
|
+
"doubleValue" : { "N" : "42.195" },
|
9
|
+
"boolValue" : { "BOOL" : true },
|
10
|
+
"listValue" : { "L":
|
11
|
+
[
|
12
|
+
{ "S" : "list-value"},
|
13
|
+
{ "N" : "123"}
|
14
|
+
]
|
15
|
+
},
|
16
|
+
"mapValue" : { "M":
|
17
|
+
{
|
18
|
+
"map-key-1" : { "S" : "map-value-1" },
|
19
|
+
"map-key-2" : { "N" : "456" }
|
20
|
+
}
|
21
|
+
}
|
22
|
+
}' \
|
23
|
+
--endpoint-url http://localhost:8000 \
|
24
|
+
--region us-east-1
|
25
|
+
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: embulk-input-dynamodb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daisuke Higashi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-04-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
@@ -64,25 +64,31 @@ files:
|
|
64
64
|
- src/main/scala/org/embulk/input/dynamodb/Filter.scala
|
65
65
|
- src/main/scala/org/embulk/input/dynamodb/FilterConfig.scala
|
66
66
|
- src/main/scala/org/embulk/input/dynamodb/PluginTask.scala
|
67
|
+
- src/main/scala/org/embulk/input/dynamodb/package.scala
|
67
68
|
- src/test/resources/json/test.json
|
68
69
|
- src/test/resources/json/test.template
|
69
70
|
- src/test/resources/yaml/authMethodBasic.yml
|
70
71
|
- src/test/resources/yaml/authMethodBasic_Error.yml
|
71
72
|
- src/test/resources/yaml/authMethodEnv.yml
|
72
73
|
- src/test/resources/yaml/authMethodProfile.yml
|
74
|
+
- src/test/resources/yaml/dynamodb-local.yml
|
73
75
|
- src/test/resources/yaml/notSetAuthMethod.yml
|
74
76
|
- src/test/scala/org/embulk/input/dynamodb/AttributeValueHelperTest.scala
|
75
77
|
- src/test/scala/org/embulk/input/dynamodb/AwsCredentialsTest.scala
|
76
|
-
-
|
78
|
+
- src/test/scala/org/embulk/input/dynamodb/DynamoDBUtilTest.scala
|
79
|
+
- test/create_table.sh
|
80
|
+
- test/put_items.sh
|
81
|
+
- test/run_dynamodb_local.sh
|
82
|
+
- classpath/commons-logging-1.1.3.jar
|
83
|
+
- classpath/commons-codec-1.6.jar
|
84
|
+
- classpath/scala-library-2.11.7.jar
|
77
85
|
- classpath/aws-java-sdk-dynamodb-1.10.43.jar
|
86
|
+
- classpath/embulk-input-dynamodb-0.1.1.jar
|
78
87
|
- classpath/aws-java-sdk-kms-1.10.43.jar
|
79
|
-
- classpath/aws-java-sdk-s3-1.10.43.jar
|
80
|
-
- classpath/commons-codec-1.6.jar
|
81
|
-
- classpath/commons-logging-1.1.3.jar
|
82
|
-
- classpath/embulk-input-dynamodb-0.1.0.jar
|
83
|
-
- classpath/httpclient-4.3.6.jar
|
84
88
|
- classpath/httpcore-4.3.3.jar
|
85
|
-
- classpath/
|
89
|
+
- classpath/httpclient-4.3.6.jar
|
90
|
+
- classpath/aws-java-sdk-core-1.10.43.jar
|
91
|
+
- classpath/aws-java-sdk-s3-1.10.43.jar
|
86
92
|
homepage: https://github.com/lulichn/embulk-input-dynamodb
|
87
93
|
licenses:
|
88
94
|
- MIT
|
@@ -107,4 +113,7 @@ rubygems_version: 2.1.9
|
|
107
113
|
signing_key:
|
108
114
|
specification_version: 4
|
109
115
|
summary: Dynamodb input plugin for Embulk
|
110
|
-
test_files:
|
116
|
+
test_files:
|
117
|
+
- test/create_table.sh
|
118
|
+
- test/put_items.sh
|
119
|
+
- test/run_dynamodb_local.sh
|