embulk-output-salesforce 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e3d02cc5822121c62af4ba8512b32674eb5b045f
4
+ data.tar.gz: d6fb63e23ab436d9fc844934e3b4dc6634f4915c
5
+ SHA512:
6
+ metadata.gz: 448501ddd1046cf41372854c03cc4fc07919f4c9969cdfa9cbcd2ddf8df436a5eb0573eb8fb6339c32e7f519a3e5769d95e6aea944e98cc482f80db094a4dd43
7
+ data.tar.gz: 667291613516bc25a989d4740d3d99a3b7a585a4ee140399208ef767f0457181ef68ad2751d64f42228b2f5c8a9735d6c0264d8c6770c6e9baf86b32bf124aa3
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ *~
2
+ /pkg/
3
+ /tmp/
4
+ *.gemspec
5
+ .gradle/
6
+ /classpath/
7
+ build/
8
+ .idea
9
+ .nb-gradle/
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+
2
+ MIT License
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining
5
+ a copy of this software and associated documentation files (the
6
+ "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish,
8
+ distribute, sublicense, and/or sell copies of the Software, and to
9
+ permit persons to whom the Software is furnished to do so, subject to
10
+ the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # Salesforce output plugin for Embulk
2
+
3
+ Embulk output plugin to load into Salesforce.com.
4
+
5
+ ## Overview
6
+
7
+ * **Plugin type**: output
8
+ * **Load all or nothing**: no
9
+ * **Resume supported**: no
10
+ * **Cleanup supported**: no
11
+
12
+ ## Configuration
13
+
14
+ - **username**: salesforce username (string, required)
15
+ - **password**: salesforce password (string, required)
16
+ - **batch_size**: batch size (string, default: 200)
17
+ - **login_endpoint**: login endpoint (string, default: https://login.salesforce.com)
18
+ - **sobject**: salesforce object API name (string, required)
19
+ - **upsert_key**: upsert API field name (string, default: null)
20
+ - **action**: output action that is "insert", "update", "upsert" or "delete" (string, required)
21
+ - **result_dir**: directory for resulting csv(success and error file) (string, default: "./target")
22
+ - **version**: API version (string, default: "34.0")
23
+
24
+ ## Example
25
+
26
+ ```yaml
27
+ out:
28
+ type: salesforce
29
+ username: hoge@example.com
30
+ password: fuga
31
+ action: insert
32
+ ```
33
+
34
+
35
+ ## Build
36
+
37
+ ```
38
+ $ ./gradlew gem
39
+ ```
data/build.gradle ADDED
@@ -0,0 +1,75 @@
1
+ plugins {
2
+ id "com.jfrog.bintray" version "1.1"
3
+ id "com.github.jruby-gradle.base" version "0.1.5"
4
+ id "java"
5
+ }
6
+ import com.github.jrubygradle.JRubyExec
7
+ repositories {
8
+ mavenCentral()
9
+ jcenter()
10
+ }
11
+ configurations {
12
+ provided
13
+ }
14
+
15
+ version = "0.1.0"
16
+
17
+ dependencies {
18
+ compile "org.embulk:embulk-core:0.6.11"
19
+ provided "org.embulk:embulk-core:0.6.11"
20
+ // compile "YOUR_JAR_DEPENDENCY_GROUP:YOUR_JAR_DEPENDENCY_MODULE:YOUR_JAR_DEPENDENCY_VERSION"
21
+ compile "net.sf.supercsv:super-csv:2.3.1"
22
+ compile files('lib/force-wsc-34.0.jar', 'lib/partner.jar')
23
+ testCompile "junit:junit:4.+"
24
+ }
25
+
26
+ task classpath(type: Copy, dependsOn: ["jar"]) {
27
+ doFirst { file("classpath").deleteDir() }
28
+ from (configurations.runtime - configurations.provided + files(jar.archivePath))
29
+ into "classpath"
30
+ }
31
+ clean { delete "classpath" }
32
+
33
+ task gem(type: JRubyExec, dependsOn: ["gemspec", "classpath"]) {
34
+ jrubyArgs "-rrubygems/gem_runner", "-eGem::GemRunner.new.run(ARGV)", "build"
35
+ script "${project.name}.gemspec"
36
+ doLast { ant.move(file: "${project.name}-${project.version}.gem", todir: "pkg") }
37
+ }
38
+
39
+ task gemPush(type: JRubyExec, dependsOn: ["gem"]) {
40
+ jrubyArgs "-rrubygems/gem_runner", "-eGem::GemRunner.new.run(ARGV)", "push"
41
+ script "pkg/${project.name}-${project.version}.gem"
42
+ }
43
+
44
+ task "package"(dependsOn: ["gemspec", "classpath"]) << {
45
+ println "> Build succeeded."
46
+ println "> You can run embulk with '-L ${file(".").absolutePath}' argument."
47
+ }
48
+
49
+ task gemspec {
50
+ ext.gemspecFile = file("${project.name}.gemspec")
51
+ inputs.file "build.gradle"
52
+ outputs.file gemspecFile
53
+ doLast { gemspecFile.write($/
54
+ Gem::Specification.new do |spec|
55
+ spec.name = "${project.name}"
56
+ spec.version = "${project.version}"
57
+ spec.authors = ["Makoto Tajitsu"]
58
+ spec.summary = %[Salesforce output plugin for Embulk]
59
+ spec.description = %[Dumps records to Salesforce.]
60
+ spec.email = ["makoto_tajitsu@hotmail.co.jp"]
61
+ spec.licenses = ["MIT"]
62
+ spec.homepage = "https://github.com/tzmfreedom/embulk-output-salesforce"
63
+
64
+ spec.files = `git ls-files`.split("\n") + Dir["classpath/*.jar"]
65
+ spec.test_files = spec.files.grep(%r"^(test|spec)/")
66
+ spec.require_paths = ["lib"]
67
+
68
+ #spec.add_dependency 'YOUR_GEM_DEPENDENCY', ['~> YOUR_GEM_DEPENDENCY_VERSION']
69
+ spec.add_development_dependency 'bundler', ['~> 1.0']
70
+ spec.add_development_dependency 'rake', ['>= 10.0']
71
+ end
72
+ /$)
73
+ }
74
+ }
75
+ clean { delete "${project.name}.gemspec" }
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,6 @@
1
+ #Wed Feb 04 13:46:12 PST 2015
2
+ distributionBase=GRADLE_USER_HOME
3
+ distributionPath=wrapper/dists
4
+ zipStoreBase=GRADLE_USER_HOME
5
+ zipStorePath=wrapper/dists
6
+ distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-bin.zip
data/gradlew ADDED
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env bash
2
+
3
+ ##############################################################################
4
+ ##
5
+ ## Gradle start up script for UN*X
6
+ ##
7
+ ##############################################################################
8
+
9
+ # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10
+ DEFAULT_JVM_OPTS=""
11
+
12
+ APP_NAME="Gradle"
13
+ APP_BASE_NAME=`basename "$0"`
14
+
15
+ # Use the maximum available, or set MAX_FD != -1 to use that value.
16
+ MAX_FD="maximum"
17
+
18
+ warn ( ) {
19
+ echo "$*"
20
+ }
21
+
22
+ die ( ) {
23
+ echo
24
+ echo "$*"
25
+ echo
26
+ exit 1
27
+ }
28
+
29
+ # OS specific support (must be 'true' or 'false').
30
+ cygwin=false
31
+ msys=false
32
+ darwin=false
33
+ case "`uname`" in
34
+ CYGWIN* )
35
+ cygwin=true
36
+ ;;
37
+ Darwin* )
38
+ darwin=true
39
+ ;;
40
+ MINGW* )
41
+ msys=true
42
+ ;;
43
+ esac
44
+
45
+ # For Cygwin, ensure paths are in UNIX format before anything is touched.
46
+ if $cygwin ; then
47
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48
+ fi
49
+
50
+ # Attempt to set APP_HOME
51
+ # Resolve links: $0 may be a link
52
+ PRG="$0"
53
+ # Need this for relative symlinks.
54
+ while [ -h "$PRG" ] ; do
55
+ ls=`ls -ld "$PRG"`
56
+ link=`expr "$ls" : '.*-> \(.*\)$'`
57
+ if expr "$link" : '/.*' > /dev/null; then
58
+ PRG="$link"
59
+ else
60
+ PRG=`dirname "$PRG"`"/$link"
61
+ fi
62
+ done
63
+ SAVED="`pwd`"
64
+ cd "`dirname \"$PRG\"`/" >&-
65
+ APP_HOME="`pwd -P`"
66
+ cd "$SAVED" >&-
67
+
68
+ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69
+
70
+ # Determine the Java command to use to start the JVM.
71
+ if [ -n "$JAVA_HOME" ] ; then
72
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73
+ # IBM's JDK on AIX uses strange locations for the executables
74
+ JAVACMD="$JAVA_HOME/jre/sh/java"
75
+ else
76
+ JAVACMD="$JAVA_HOME/bin/java"
77
+ fi
78
+ if [ ! -x "$JAVACMD" ] ; then
79
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80
+
81
+ Please set the JAVA_HOME variable in your environment to match the
82
+ location of your Java installation."
83
+ fi
84
+ else
85
+ JAVACMD="java"
86
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87
+
88
+ Please set the JAVA_HOME variable in your environment to match the
89
+ location of your Java installation."
90
+ fi
91
+
92
+ # Increase the maximum file descriptors if we can.
93
+ if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94
+ MAX_FD_LIMIT=`ulimit -H -n`
95
+ if [ $? -eq 0 ] ; then
96
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97
+ MAX_FD="$MAX_FD_LIMIT"
98
+ fi
99
+ ulimit -n $MAX_FD
100
+ if [ $? -ne 0 ] ; then
101
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
102
+ fi
103
+ else
104
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105
+ fi
106
+ fi
107
+
108
+ # For Darwin, add options to specify how the application appears in the dock
109
+ if $darwin; then
110
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111
+ fi
112
+
113
+ # For Cygwin, switch paths to Windows format before running java
114
+ if $cygwin ; then
115
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117
+
118
+ # We build the pattern for arguments to be converted via cygpath
119
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120
+ SEP=""
121
+ for dir in $ROOTDIRSRAW ; do
122
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
123
+ SEP="|"
124
+ done
125
+ OURCYGPATTERN="(^($ROOTDIRS))"
126
+ # Add a user-defined pattern to the cygpath arguments
127
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129
+ fi
130
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
131
+ i=0
132
+ for arg in "$@" ; do
133
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135
+
136
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138
+ else
139
+ eval `echo args$i`="\"$arg\""
140
+ fi
141
+ i=$((i+1))
142
+ done
143
+ case $i in
144
+ (0) set -- ;;
145
+ (1) set -- "$args0" ;;
146
+ (2) set -- "$args0" "$args1" ;;
147
+ (3) set -- "$args0" "$args1" "$args2" ;;
148
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154
+ esac
155
+ fi
156
+
157
+ # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158
+ function splitJvmOpts() {
159
+ JVM_OPTS=("$@")
160
+ }
161
+ eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162
+ JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163
+
164
+ exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
data/gradlew.bat ADDED
@@ -0,0 +1,90 @@
1
+ @if "%DEBUG%" == "" @echo off
2
+ @rem ##########################################################################
3
+ @rem
4
+ @rem Gradle startup script for Windows
5
+ @rem
6
+ @rem ##########################################################################
7
+
8
+ @rem Set local scope for the variables with windows NT shell
9
+ if "%OS%"=="Windows_NT" setlocal
10
+
11
+ @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12
+ set DEFAULT_JVM_OPTS=
13
+
14
+ set DIRNAME=%~dp0
15
+ if "%DIRNAME%" == "" set DIRNAME=.
16
+ set APP_BASE_NAME=%~n0
17
+ set APP_HOME=%DIRNAME%
18
+
19
+ @rem Find java.exe
20
+ if defined JAVA_HOME goto findJavaFromJavaHome
21
+
22
+ set JAVA_EXE=java.exe
23
+ %JAVA_EXE% -version >NUL 2>&1
24
+ if "%ERRORLEVEL%" == "0" goto init
25
+
26
+ echo.
27
+ echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28
+ echo.
29
+ echo Please set the JAVA_HOME variable in your environment to match the
30
+ echo location of your Java installation.
31
+
32
+ goto fail
33
+
34
+ :findJavaFromJavaHome
35
+ set JAVA_HOME=%JAVA_HOME:"=%
36
+ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37
+
38
+ if exist "%JAVA_EXE%" goto init
39
+
40
+ echo.
41
+ echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42
+ echo.
43
+ echo Please set the JAVA_HOME variable in your environment to match the
44
+ echo location of your Java installation.
45
+
46
+ goto fail
47
+
48
+ :init
49
+ @rem Get command-line arguments, handling Windowz variants
50
+
51
+ if not "%OS%" == "Windows_NT" goto win9xME_args
52
+ if "%@eval[2+2]" == "4" goto 4NT_args
53
+
54
+ :win9xME_args
55
+ @rem Slurp the command line arguments.
56
+ set CMD_LINE_ARGS=
57
+ set _SKIP=2
58
+
59
+ :win9xME_args_slurp
60
+ if "x%~1" == "x" goto execute
61
+
62
+ set CMD_LINE_ARGS=%*
63
+ goto execute
64
+
65
+ :4NT_args
66
+ @rem Get arguments from the 4NT Shell from JP Software
67
+ set CMD_LINE_ARGS=%$
68
+
69
+ :execute
70
+ @rem Setup the command line
71
+
72
+ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73
+
74
+ @rem Execute Gradle
75
+ "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76
+
77
+ :end
78
+ @rem End local scope for the variables with windows NT shell
79
+ if "%ERRORLEVEL%"=="0" goto mainEnd
80
+
81
+ :fail
82
+ rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83
+ rem the _cmd.exe /c_ return code!
84
+ if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85
+ exit /b 1
86
+
87
+ :mainEnd
88
+ if "%OS%"=="Windows_NT" endlocal
89
+
90
+ :omega
@@ -0,0 +1,3 @@
1
+ Embulk::JavaPlugin.register_output(
2
+ "salesforce", "org.embulk.output.SalesforceOutputPlugin",
3
+ File.expand_path('../../../../classpath', __FILE__))
Binary file
data/lib/partner.jar ADDED
Binary file
@@ -0,0 +1,478 @@
1
+ package org.embulk.output;
2
+
3
+ import com.google.common.base.Optional;
4
+ import com.sforce.soap.partner.Connector;
5
+ import com.sforce.soap.partner.DeleteResult;
6
+ import com.sforce.soap.partner.GetUserInfoResult;
7
+ import com.sforce.soap.partner.PartnerConnection;
8
+ import com.sforce.soap.partner.SaveResult;
9
+ import com.sforce.soap.partner.UpsertResult;
10
+ import com.sforce.soap.partner.sobject.SObject;
11
+ import com.sforce.ws.ConnectionException;
12
+ import com.sforce.ws.ConnectorConfig;
13
+ import java.io.FileWriter;
14
+ import java.io.IOException;
15
+ import java.text.SimpleDateFormat;
16
+ import java.util.ArrayList;
17
+ import java.util.Calendar;
18
+ import java.util.Date;
19
+ import java.util.List;
20
+ import org.embulk.config.CommitReport;
21
+ import org.embulk.config.Config;
22
+ import org.embulk.config.ConfigDefault;
23
+ import org.embulk.config.ConfigDiff;
24
+ import org.embulk.config.ConfigSource;
25
+ import org.embulk.config.Task;
26
+ import org.embulk.config.TaskSource;
27
+ import org.embulk.spi.Column;
28
+ import org.embulk.spi.ColumnVisitor;
29
+ import org.embulk.spi.Exec;
30
+ import org.embulk.spi.OutputPlugin;
31
+ import org.embulk.spi.Page;
32
+ import org.embulk.spi.PageReader;
33
+ import org.embulk.spi.Schema;
34
+ import org.embulk.spi.TransactionalPageOutput;
35
+ import org.joda.time.DateTime;
36
+ import org.slf4j.Logger;
37
+ import org.supercsv.cellprocessor.ift.CellProcessor;
38
+ import org.supercsv.io.CsvListWriter;
39
+ import org.supercsv.io.ICsvListWriter;
40
+ import org.supercsv.prefs.CsvPreference;
41
+
42
+ public class SalesforceOutputPlugin
43
+ implements OutputPlugin
44
+ {
45
+ protected static Logger logger;
46
+ private static PartnerConnection client = null;
47
+
48
+ public interface PluginTask
49
+ extends Task
50
+ {
51
+ @Config("username")
52
+ public String getUsername();
53
+
54
+ @Config("password")
55
+ public String getPassword();
56
+
57
+ @Config("login_endpoint")
58
+ @ConfigDefault("https://login.salesforce.com")
59
+ public Optional<String> getLoginEndpoint();
60
+
61
+ @Config("sobject")
62
+ public String getSObject();
63
+
64
+ @Config("upsert_key")
65
+ @ConfigDefault("null")
66
+ public Optional<String> getUpsertKey();
67
+
68
+ @Config("batch_size")
69
+ @ConfigDefault("200")
70
+ public Integer getBatchSize();
71
+
72
+ @Config("action")
73
+ @ConfigDefault("\"insert\"")
74
+ public Optional<String> getAction();
75
+
76
+ @Config("version")
77
+ @ConfigDefault("34.0")
78
+ public Optional<String> getVersion();
79
+
80
+ @Config("result_dir")
81
+ @ConfigDefault("\"./target\"")
82
+ public Optional<String> getResultDir();
83
+ }
84
+
85
+ @Override
86
+ public ConfigDiff transaction(ConfigSource config,
87
+ Schema schema, int taskCount,
88
+ OutputPlugin.Control control)
89
+ {
90
+ PluginTask task = config.loadConfig(PluginTask.class);
91
+ logger = Exec.getLogger(getClass());
92
+ final String username = task.getUsername();
93
+ final String password = task.getPassword();
94
+ final String loginEndpoint = task.getLoginEndpoint().get();
95
+ try {
96
+ if (client == null) {
97
+ ConnectorConfig connectorConfig = new ConnectorConfig();
98
+ connectorConfig.setUsername(username);
99
+ connectorConfig.setPassword(password);
100
+ connectorConfig.setAuthEndpoint(loginEndpoint + "/services/Soap/u/" +task.getVersion().get() + "/");
101
+
102
+ client = Connector.newConnection(connectorConfig);
103
+ GetUserInfoResult userInfo = client.getUserInfo();
104
+ logger.info("login successful with {}", userInfo.getUserName());
105
+ }
106
+ } catch(ConnectionException ex) {
107
+ logger.error("Login error. Please check your credentials.");
108
+ throw new RuntimeException(ex);
109
+ }
110
+
111
+ control.run(task.dump());
112
+ return Exec.newConfigDiff();
113
+ }
114
+
115
+ @Override
116
+ public ConfigDiff resume(TaskSource taskSource,
117
+ Schema schema, int taskCount,
118
+ OutputPlugin.Control control)
119
+ {
120
+ throw new UnsupportedOperationException("salesforce output plugin does not support resuming");
121
+ }
122
+
123
+ @Override
124
+ public void cleanup(TaskSource taskSource,
125
+ Schema schema, int taskCount,
126
+ List<CommitReport> successCommitReports)
127
+ {
128
+ logger.info("logout");
129
+ try {
130
+ if (client != null) {
131
+ client.logout();
132
+ }
133
+ } catch (ConnectionException ex) {}
134
+ }
135
+
136
+ @Override
137
+ public TransactionalPageOutput open(TaskSource taskSource, Schema schema, int taskIndex)
138
+ {
139
+ PluginTask task = taskSource.loadTask(PluginTask.class);
140
+
141
+ PageReader reader = new PageReader(schema);
142
+ return new SalesforcePageOutput(reader, client, task);
143
+ }
144
+
145
+ public class SalesforcePageOutput
146
+ implements TransactionalPageOutput
147
+ {
148
+ private final String datePostFix = new SimpleDateFormat("yyyyMMddhhmmssSSS").format(new Date());
149
+
150
+ private final PageReader pageReader;
151
+ private final PartnerConnection client;
152
+ private final int batchSize;
153
+ private List<SObject> records;
154
+ private final String upsertKey;
155
+ private final String sobject;
156
+ private final String action;
157
+ private final String resultDir;
158
+ private Integer numOfSuccess = 0;
159
+ private Integer numOfError = 0;
160
+
161
+ public SalesforcePageOutput(final PageReader pageReader,
162
+ PartnerConnection client, PluginTask task)
163
+ {
164
+ this.pageReader = pageReader;
165
+ this.client = client;
166
+ this.batchSize = task.getBatchSize();
167
+ this.upsertKey = task.getUpsertKey().isPresent() ? task.getUpsertKey().get() : null;
168
+ this.sobject = task.getSObject();
169
+ this.action = task.getAction().isPresent() ? task.getAction().get() : null;
170
+ this.resultDir = task.getResultDir().isPresent() ? task.getResultDir().get() : null;
171
+ this.records = new ArrayList<>();
172
+ }
173
+
174
+ @Override
175
+ public void add(Page page)
176
+ {
177
+ try {
178
+ pageReader.setPage(page);
179
+ while (pageReader.nextRecord()) {
180
+ final SObject record = new SObject();
181
+ record.setType(this.sobject);
182
+
183
+ pageReader.getSchema().visitColumns(new ColumnVisitor() {
184
+ @Override
185
+ public void doubleColumn(Column column) {
186
+ record.addField(column.getName(), pageReader.getDouble(column));
187
+ }
188
+ @Override
189
+ public void timestampColumn(Column column) {
190
+ DateTime dt = new DateTime(pageReader.getTimestamp(column).getEpochSecond()*1000);
191
+ Calendar cal = Calendar.getInstance();
192
+ cal.clear();
193
+ cal.setTimeZone(dt.getZone().toTimeZone());
194
+ cal.set(dt.getYear(), dt.getMonthOfYear()-1, dt.getDayOfMonth(),
195
+ dt.getHourOfDay(), dt.getMinuteOfHour(), dt.getSecondOfMinute());
196
+ record.addField(column.getName(), cal);
197
+ }
198
+ @Override
199
+ public void stringColumn(Column column) {
200
+ record.addField(column.getName(), pageReader.getString(column));
201
+ }
202
+ @Override
203
+ public void longColumn(Column column) {
204
+ record.addField(column.getName(), pageReader.getLong(column));
205
+ }
206
+ @Override
207
+ public void booleanColumn(Column column) {
208
+ record.addField(column.getName(), pageReader.getBoolean(column));
209
+ }
210
+
211
+ });
212
+ this.records.add(record);
213
+
214
+ if (this.records.size() >= this.batchSize) {
215
+ this.action(this.records);
216
+ logger.info("Number of processed records: {}", this.numOfSuccess + this.numOfError);
217
+ }
218
+ }
219
+
220
+ if (!this.records.isEmpty()) {
221
+ this.action(this.records);
222
+ logger.info("Number of processed records: {}", this.numOfSuccess + this.numOfError);
223
+ }
224
+ } catch (ConnectionException ex) {
225
+ logger.error("Connection Error: {}", ex.getMessage());
226
+ } catch (IOException ex) {
227
+ throw new RuntimeException(ex);
228
+ }
229
+ }
230
+
231
+ @Override
232
+ public void finish()
233
+ {
234
+
235
+ }
236
+
237
+ @Override
238
+ public void close()
239
+ {
240
+
241
+ }
242
+
243
+ @Override
244
+ public void abort()
245
+ {
246
+ }
247
+
248
+ @Override
249
+ public CommitReport commit()
250
+ {
251
+ return Exec.newCommitReport();
252
+ }
253
+
254
+ private void action(List<SObject> records) throws ConnectionException, IOException{
255
+ switch(this.action){
256
+ case "insert":
257
+ SaveResult[] insertResults = client.create(
258
+ (SObject[])this.records.toArray(new SObject[0]));
259
+ countSaveResults(insertResults);
260
+ if (resultDir != null) {
261
+ ResultWrapper resultWrapper = new ResultWrapper(insertResults, null, null);
262
+ createResultsFiles(records, resultWrapper);
263
+ }
264
+ break;
265
+ case "upsert":
266
+ UpsertResult[] upsertResults = client.upsert(
267
+ this.upsertKey, (SObject[])this.records.toArray(new SObject[0]));
268
+ countUpsertResults(upsertResults);
269
+ if (resultDir != null) {
270
+ ResultWrapper resultWrapper = new ResultWrapper(null, upsertResults, null);
271
+ createResultsFiles(records, resultWrapper);
272
+ }
273
+ break;
274
+ case "update":
275
+ SaveResult[] updateResults = client.update(
276
+ (SObject[])this.records.toArray(new SObject[0]));
277
+ countSaveResults(updateResults);
278
+ if (resultDir != null) {
279
+ ResultWrapper resultWrapper = new ResultWrapper(updateResults, null, null);
280
+ createResultsFiles(records, resultWrapper);
281
+ }
282
+ break;
283
+ case "delete":
284
+ List<String> ids = new ArrayList<>();
285
+ for (SObject sobj : this.records) {
286
+ ids.add(sobj.getId());
287
+ }
288
+ DeleteResult[] deleteResults = client.delete(ids.toArray(new String[0]));
289
+ countDeleteResults(deleteResults);
290
+ if (resultDir != null) {
291
+ ResultWrapper resultWrapper = new ResultWrapper(null, null, deleteResults);
292
+ createResultsFiles(records, resultWrapper);
293
+ }
294
+ break;
295
+ }
296
+ this.records = new ArrayList<>();
297
+ }
298
+
299
+ private void countSaveResults(SaveResult[] saveResults) {
300
+ for (SaveResult saveResult : saveResults) {
301
+ if (saveResult.isSuccess()) {
302
+ this.numOfSuccess++;
303
+ } else {
304
+ this.numOfError++;
305
+ }
306
+ }
307
+ }
308
+
309
+ private void countUpsertResults(UpsertResult[] upsertResults) {
310
+ for (UpsertResult upsertResult : upsertResults) {
311
+ if (upsertResult.isSuccess()) {
312
+ this.numOfSuccess++;
313
+ } else {
314
+ this.numOfError++;
315
+ }
316
+ }
317
+ }
318
+
319
+ private void countDeleteResults(DeleteResult[] deleteResults) {
320
+ for (DeleteResult deleteResult : deleteResults) {
321
+ if (deleteResult.isSuccess()) {
322
+ this.numOfSuccess++;
323
+ } else {
324
+ this.numOfError++;
325
+ }
326
+ }
327
+ }
328
+
329
+ private List<String> createSuccessHeader() {
330
+ final List<String> successHeader = new ArrayList<>();
331
+ successHeader.add("Id");
332
+ for (Column col : pageReader.getSchema().getColumns()) {
333
+ successHeader.add(col.getName());
334
+ }
335
+ return successHeader;
336
+ }
337
+
338
+ private List<String> createErrorHeader() {
339
+ final List<String> errorHeader = new ArrayList<>();
340
+ for (Column col : pageReader.getSchema().getColumns()) {
341
+ errorHeader.add(col.getName());
342
+ }
343
+ errorHeader.add("Error");
344
+ return errorHeader;
345
+ }
346
+
347
+ private void createResultsFiles(List<SObject> records, ResultWrapper resultWrapper) throws IOException{
348
+ ICsvListWriter successListWriter = null;
349
+ ICsvListWriter errorListWriter = null;
350
+ try {
351
+ String successFileName = this.resultDir + "/success_" + datePostFix + ".csv";
352
+ successListWriter = new CsvListWriter(new FileWriter(successFileName, true),
353
+ CsvPreference.STANDARD_PREFERENCE);
354
+ //successListWriter.write(createSuccessHeader());
355
+
356
+ String errorFileName = this.resultDir + "/error_" + datePostFix + ".csv";
357
+ errorListWriter = new CsvListWriter(new FileWriter(errorFileName, true),
358
+ CsvPreference.STANDARD_PREFERENCE);
359
+ //errorListWriter.write(createErrorHeader());
360
+
361
+ CellProcessor[] processors = new CellProcessor[pageReader.getSchema().getColumns().size() + 1];
362
+ ArrayList<ArrayList<String>> errorValues = new ArrayList<>();
363
+ for (Integer i = 0, imax = records.size(); i < imax; i++) {
364
+ SObject record = records.get(i);
365
+
366
+ List<String> values = new ArrayList<>();
367
+ if (resultWrapper.getIsSuccess(i)) {
368
+ values.add(resultWrapper.getId(i));
369
+ }
370
+ for (Column col : pageReader.getSchema().getColumns()) {
371
+ Object obj = record.getSObjectField(col.getName());
372
+ if (obj != null) {
373
+ if (obj instanceof Calendar) {
374
+ DateTime dt = new DateTime((Calendar)obj);
375
+ values.add(dt.toString("yyyy-MM-dd'T'hh:mm:ssZZ"));
376
+ } else {
377
+ values.add(obj.toString());
378
+ }
379
+ } else {
380
+ values.add("");
381
+ }
382
+ }
383
+ if (!resultWrapper.getIsSuccess(i)) {
384
+ StringBuilder sb = new StringBuilder();
385
+ for (com.sforce.soap.partner.Error err : resultWrapper.getErrors(i)) {
386
+ if (sb.length() > 0) {
387
+ sb.append(";");
388
+ }
389
+ sb.append(err.getStatusCode())
390
+ .append(":")
391
+ .append(err.getMessage());
392
+ }
393
+
394
+ values.add(sb.toString());
395
+ }
396
+ if (resultWrapper.getIsSuccess(i)) {
397
+ successListWriter.write(values);
398
+ } else {
399
+ errorListWriter.write(values);
400
+ }
401
+ }
402
+ } finally {
403
+ if(successListWriter != null ) {
404
+ successListWriter.close();
405
+ }
406
+ if(errorListWriter != null ) {
407
+ errorListWriter.close();
408
+ }
409
+ }
410
+ }
411
+ }
412
+
413
+ public class ResultWrapper {
414
+ private final SaveResult[] saveResult;
415
+ private final UpsertResult[] upsertResult;
416
+ private final DeleteResult[] deleteResult;
417
+
418
+ public ResultWrapper(SaveResult[] saveResults,
419
+ UpsertResult[] upsertResults, DeleteResult[] deleteResults) {
420
+ this.saveResult = saveResults;
421
+ this.upsertResult = upsertResults;
422
+ this.deleteResult = deleteResults;
423
+ }
424
+
425
+ public Boolean isSaveResult() {
426
+ return this.saveResult != null;
427
+ }
428
+
429
+ public Boolean isUpsertResult() {
430
+ return this.upsertResult != null;
431
+ }
432
+
433
+ public Boolean isDeleteResult() {
434
+ return this.deleteResult != null;
435
+ }
436
+
437
+ public String getId(Integer index) {
438
+ if (this.isSaveResult()) {
439
+ return this.saveResult[index].getId();
440
+ } else if (this.isUpsertResult()) {
441
+ return this.upsertResult[index].getId();
442
+ } else if (this.isDeleteResult()) {
443
+ return this.deleteResult[index].getId();
444
+ }
445
+ return null;
446
+ }
447
+
448
+ public Boolean getIsSuccess(Integer index) {
449
+ if (this.isSaveResult()) {
450
+ return this.saveResult[index].isSuccess();
451
+ } else if (this.isUpsertResult()) {
452
+ return this.upsertResult[index].isSuccess();
453
+ } else if (this.isDeleteResult()) {
454
+ return this.deleteResult[index].isSuccess();
455
+ }
456
+ return null;
457
+ }
458
+
459
+ public Boolean getIsCreated(Integer index) {
460
+ if (this.isUpsertResult()) {
461
+ return this.upsertResult[index].isCreated();
462
+ }
463
+ return null;
464
+ }
465
+
466
+ public com.sforce.soap.partner.Error[] getErrors(Integer index) {
467
+ if (this.isSaveResult()) {
468
+ return this.saveResult[index].getErrors();
469
+ } else if (this.isUpsertResult()) {
470
+ return this.upsertResult[index].getErrors();
471
+ } else if (this.isDeleteResult()) {
472
+ return this.deleteResult[index].getErrors();
473
+ }
474
+ return null;
475
+ }
476
+ }
477
+
478
+ }
@@ -0,0 +1,5 @@
1
+ package org.embulk.output;
2
+
3
+ public class TestSalesforceOutputPlugin
4
+ {
5
+ }
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: embulk-output-salesforce
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Makoto Tajitsu
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-06-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ version_requirements: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ requirement: !ruby/object:Gem::Requirement
21
+ requirements:
22
+ - - ~>
23
+ - !ruby/object:Gem::Version
24
+ version: '1.0'
25
+ prerelease: false
26
+ type: :development
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '10.0'
39
+ prerelease: false
40
+ type: :development
41
+ description: Dumps records to Salesforce.
42
+ email:
43
+ - makoto_tajitsu@hotmail.co.jp
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - .gitignore
49
+ - LICENSE.txt
50
+ - README.md
51
+ - build.gradle
52
+ - gradle/wrapper/gradle-wrapper.jar
53
+ - gradle/wrapper/gradle-wrapper.properties
54
+ - gradlew
55
+ - gradlew.bat
56
+ - lib/embulk/output/salesforce.rb
57
+ - lib/force-wsc-34.0.jar
58
+ - lib/partner.jar
59
+ - src/main/java/org/embulk/output/SalesforceOutputPlugin.java
60
+ - src/test/java/org/embulk/output/TestSalesforceOutputPlugin.java
61
+ - classpath/embulk-output-salesforce-0.1.0.jar
62
+ - classpath/force-wsc-34.0.jar
63
+ - classpath/partner.jar
64
+ - classpath/super-csv-2.3.1.jar
65
+ homepage: https://github.com/tzmfreedom/embulk-output-salesforce
66
+ licenses:
67
+ - MIT
68
+ metadata: {}
69
+ post_install_message:
70
+ rdoc_options: []
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ requirements: []
84
+ rubyforge_project:
85
+ rubygems_version: 2.1.9
86
+ signing_key:
87
+ specification_version: 4
88
+ summary: Salesforce output plugin for Embulk
89
+ test_files: []