diff --git a/.gitignore b/.gitignore
index 356845ca..3d657b30 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,3 +19,4 @@ common/
_layouts/
public/
log.txt
+/api-key.txt
diff --git a/README.md b/README.md
index 0f3e4db4..040ca4be 100644
--- a/README.md
+++ b/README.md
@@ -39,7 +39,7 @@ This repository is our development basecamp. If you find a bug or have questions
### Apache Maven
DataSync uses Maven for building and package management. For more information: [What is Maven?](http://maven.apache.org/what-is-maven.html)
-To build the project run:
+To build the project, first you'll need to create an application token on your profile page. Put the random string it produces in a file called "api-key.txt" in the root directory of this project, then run
```
mvn clean install
```
@@ -57,5 +57,6 @@ java -jar DataSync-1.8.2-jar-with-dependencies.jar
### Java SDK
-DataSync can be used as a Java SDK, for detailed documentation refer to:
+DataSync can be used as a Java SDK, for detailed documentation refer
+to:
[http://socrata.github.io/datasync/guides/datasync-library-sdk.html](http://socrata.github.io/datasync/guides/datasync-library-sdk.html)
diff --git a/pom.xml b/pom.xml
index 932db3c8..59821784 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,7 +7,7 @@
4.0.0
DataSync
DataSync
- 1.8.2
+ 1.9.0
Ayn Leslie-Cook
@@ -60,11 +60,25 @@
images/
+
+ .
+ true
+
+ api-key.txt
+
+
src/test/java
+
+ .
+ true
+
+ api-key.txt
+
+
@@ -97,7 +111,7 @@
com.socrata
soda-api-java
- 0.9.12
+ 0.10.1
com.socrata
@@ -110,14 +124,19 @@
1.4.7
- org.codehaus.jackson
- jackson-core-asl
- 1.9.13
+ com.fasterxml.jackson.core
+ jackson-core
+ 2.8.6
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.8.6
- org.codehaus.jackson
- jackson-mapper-asl
- 1.9.13
+ com.fasterxml.jackson.core
+ jackson-annotations
+ 2.8.6
org.tukaani
@@ -180,5 +199,10 @@
javac2
7.0.3
+
+ info.debatty
+ java-string-similarity
+ 1.1.0
+
diff --git a/src/main/java/com/socrata/datasync/DatasetUtils.java b/src/main/java/com/socrata/datasync/DatasetUtils.java
index 4aa48cb8..78ae2a42 100644
--- a/src/main/java/com/socrata/datasync/DatasetUtils.java
+++ b/src/main/java/com/socrata/datasync/DatasetUtils.java
@@ -1,8 +1,10 @@
package com.socrata.datasync;
+import au.com.bytecode.opencsv.CSVReader;
import com.socrata.datasync.config.userpreferences.UserPreferences;
import com.socrata.model.importer.Column;
import com.socrata.model.importer.Dataset;
+import com.socrata.model.importer.GeoDataset;
import com.socrata.model.importer.DatasetInfo;
import org.apache.http.HttpException;
import org.apache.http.HttpStatus;
@@ -14,42 +16,50 @@
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.utils.URIBuilder;
-import org.codehaus.jackson.map.DeserializationConfig;
-import org.codehaus.jackson.map.ObjectMapper;
+import com.fasterxml.jackson.databind.DeserializationConfig;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
+import java.io.StringReader;
import java.net.URI;
import java.net.URISyntaxException;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class DatasetUtils {
+ private static final String LOCATION_DATATYPE_NAME = "location";
- private static class DatasetInfoResponseHandler implements ResponseHandler {
- @Override
- public DatasetInfo handleResponse(final HttpResponse response)
- throws ClientProtocolException, IOException {
-
- StatusLine statusLine = response.getStatusLine();
- int status = statusLine.getStatusCode();
- if (status >= 200 && status < 300) {
- HttpEntity entity = response.getEntity();
- return entity != null ? mapper.readValue(entity.getContent(), DatasetInfo.class) : null;
- } else {
- throw new ClientProtocolException(statusLine.toString());
- }
- }
- }
+ private static ObjectMapper mapper = new ObjectMapper().enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
+ public static Dataset getDatasetInfo(UserPreferences userPrefs, String viewId) throws URISyntaxException, IOException, HttpException {
+ Dataset ds = getDatasetInfoReflective(userPrefs, viewId, Dataset.class);
+ removeSystemAndComputedColumns(ds);
+ return ds;
+ }
- private static final String LOCATION_DATATYPE_NAME = "location";
+ public static GeoDataset getGeoDatasetInfo(UserPreferences userPrefs, String viewId) throws URISyntaxException, IOException, HttpException {
+ return getDatasetInfoReflective(userPrefs, viewId, GeoDataset.class);
+ }
- private static ObjectMapper mapper = new ObjectMapper().enable(DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
+ private static void removeSystemAndComputedColumns(Dataset ds) {
+ List columns = ds.getColumns();
+ Iterator it = columns.iterator();
+ while(it.hasNext()) {
+ Column c = it.next();
+ if(c.getFieldName().startsWith(":") || c.getComputationStrategy() != null) {
+ it.remove();
+ }
+ }
+ ds.setColumns(columns);
+ }
- public static T getDatasetInfo(UserPreferences userPrefs, String viewId, final Class typ) throws URISyntaxException, IOException, HttpException {
+ private static T getDatasetInfoReflective(UserPreferences userPrefs, String viewId, final Class typ) throws URISyntaxException, IOException, HttpException {
String justDomain = getDomainWithoutScheme(userPrefs);
URI absolutePath = new URIBuilder()
.setScheme("https")
@@ -78,12 +88,12 @@ public T handleResponse(
return datasetInfo;
}
- public static String getDatasetSample(UserPreferences userPrefs, String viewId, int rowsToSample) throws URISyntaxException, IOException, HttpException {
+ public static List> getDatasetSample(UserPreferences userPrefs, Dataset dataset, int rowsToSample) throws URISyntaxException, IOException, HttpException {
String justDomain = getDomainWithoutScheme(userPrefs);
URI absolutePath = new URIBuilder()
.setScheme("https")
.setHost(justDomain)
- .setPath("/resource/" + viewId + ".csv")
+ .setPath("/resource/" + dataset.getId() + ".csv")
.addParameter("$limit",""+rowsToSample)
.build();
@@ -105,7 +115,37 @@ public String handleResponse(
HttpUtility util = new HttpUtility(userPrefs, true);
String sample = util.get(absolutePath, "application/csv", handler);
util.close();
- return sample;
+
+ CSVReader reader = new CSVReader(new StringReader(sample));
+
+ List> results = new ArrayList<>();
+
+ Set expectedFieldNames = new HashSet();
+ for(Column c : dataset.getColumns()) {
+ expectedFieldNames.add(c.getFieldName());
+ }
+ String[] row = reader.readNext();
+ boolean[] keep = new boolean[row.length];
+ for(int i = 0; i != row.length; ++i) {
+ keep[i] = expectedFieldNames.contains(row[i]);
+ }
+ results.add(filter(keep, row));
+
+ while((row = reader.readNext()) != null) {
+ results.add(filter(keep, row));
+ }
+
+ return results;
+ }
+
+ private static List filter(boolean[] filter, String[] elems) {
+ List result = new ArrayList<>();
+
+ for(int i = 0; i != elems.length; ++i) {
+ if(filter[i]) result.add(elems[i]);
+ }
+
+ return result;
}
public static String getDomainWithoutScheme(UserPreferences userPrefs){
@@ -140,7 +180,7 @@ public static String getRowIdentifierName(Dataset schema) {
* @return list of field names or null if there
*/
public static String getFieldNamesString(UserPreferences userPrefs, String datasetId) throws HttpException, IOException, URISyntaxException {
- Dataset datasetInfo = getDatasetInfo(userPrefs, datasetId, Dataset.class);
+ Dataset datasetInfo = getDatasetInfo(userPrefs, datasetId);
return getFieldNamesString(datasetInfo);
}
diff --git a/src/main/java/com/socrata/datasync/DatasyncGithubRelease.java b/src/main/java/com/socrata/datasync/DatasyncGithubRelease.java
index a34d1d5e..954bb17a 100644
--- a/src/main/java/com/socrata/datasync/DatasyncGithubRelease.java
+++ b/src/main/java/com/socrata/datasync/DatasyncGithubRelease.java
@@ -1,8 +1,8 @@
package com.socrata.datasync;
-import org.codehaus.jackson.annotate.JsonProperty;
-import org.codehaus.jackson.map.annotate.JsonSerialize;
-import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown=true)
diff --git a/src/main/java/com/socrata/datasync/HttpUtility.java b/src/main/java/com/socrata/datasync/HttpUtility.java
index 150b635b..d58e5c4b 100644
--- a/src/main/java/com/socrata/datasync/HttpUtility.java
+++ b/src/main/java/com/socrata/datasync/HttpUtility.java
@@ -70,7 +70,7 @@ public HttpUtility(UserPreferences userPrefs, boolean useAuth, int maxRetries, d
HttpClientBuilder clientBuilder = HttpClients.custom();
if (useAuth) {
authHeader = getAuthHeader(userPrefs.getUsername(), userPrefs.getPassword());
- appToken = userPrefs.getAPIKey();
+ appToken = userPrefs.getConnectionInfo().getToken();
}
authRequired = useAuth;
if(userPrefs != null) {
diff --git a/src/main/java/com/socrata/datasync/Main.java b/src/main/java/com/socrata/datasync/Main.java
index 4f52b3c8..98ff93dd 100644
--- a/src/main/java/com/socrata/datasync/Main.java
+++ b/src/main/java/com/socrata/datasync/Main.java
@@ -1,129 +1,129 @@
-package com.socrata.datasync;
-
-import com.socrata.datasync.job.Job;
-import com.socrata.datasync.job.Jobs;
-import com.socrata.datasync.job.LoadPreferencesJob;
-import com.socrata.datasync.job.PortJob;
-import com.socrata.datasync.job.GISJob;
-import com.socrata.datasync.job.GISJob.ControlDisagreementException;
-import com.socrata.datasync.config.CommandLineOptions;
-import com.socrata.datasync.config.userpreferences.UserPreferences;
-import com.socrata.datasync.config.userpreferences.UserPreferencesFile;
-import com.socrata.datasync.config.userpreferences.UserPreferencesJava;
-import com.socrata.datasync.ui.SimpleIntegrationWizard;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Arrays;
-
-import org.apache.commons.cli.CommandLine;
-import org.apache.commons.cli.HelpFormatter;
-import org.apache.commons.cli.ParseException;
-import org.codehaus.jackson.map.ObjectMapper;
-
-public class Main {
- /**
- * Loads an instance of the SimpleIntegrationWizard in command line
- * mode (if arguments are given) or as a GUI (if no arguments are given).
- */
- public static void main(String[] args) throws ParseException, ControlDisagreementException {
- if(args.length == 0) {
- // Open GUI (default)
- new SimpleIntegrationWizard();
- } else if(args.length == 1) {
- if (args[0].equals("-?") || args[0].equals("--help")) {
- printHelp();
- } else if (args[0].equals("-v") || args[0].equals("--version")) {
- System.out.println("DataSync version " + VersionProvider.getThisVersion());
- } else {
- // Run a job file (.sij) in command-line mode
- String jobFileToRun = args[0];
-
- new SimpleIntegrationRunner(jobFileToRun);
- }
- } else {
- // generate & run job from command line args
- checkVersion();
-
- CommandLineOptions options = new CommandLineOptions();
- CommandLine cmd = options.getCommandLine(args);
- UserPreferences userPrefs = null;
- try {
- userPrefs = loadUserPreferences(options, cmd);
- } catch (IOException e) {
- System.err.println("Failed to load configuration: " + e.toString());
- System.exit(1);
- }
-
- String jobTypeFlag = options.JOB_TYPE_FLAG;
- String jobType = cmd.getOptionValue(jobTypeFlag, options.DEFAULT_JOBTYPE);
-
- Job jobToRun = new com.socrata.datasync.job.IntegrationJob(userPrefs);
- if(jobType.equals(Jobs.PORT_JOB.toString())) {
- jobToRun = new PortJob(userPrefs);
- } else if(jobType.equals(Jobs.GIS_JOB.toString())){
- jobToRun = new GISJob(userPrefs);
- } else if(jobType.equals(Jobs.LOAD_PREFERENCES_JOB.toString())) {
- jobToRun = new LoadPreferencesJob(userPrefs);
- } else if (!jobType.equals(Jobs.INTEGRATION_JOB.toString())){
- System.err.println("Invalid " + jobTypeFlag + ": " + cmd.getOptionValue(jobTypeFlag) +
- " (must be " + Arrays.toString(Jobs.values()) + ")");
- System.exit(1);
- }
-
- if (jobToRun.validateArgs(cmd)) {
- jobToRun.configure(cmd);
- new SimpleIntegrationRunner(jobToRun);
- } else {
- printHelp();
- System.exit(1);
- }
- }
- }
-
- private static void printHelp() {
- HelpFormatter formatter = new HelpFormatter();
- formatter.printHelp("DataSync", CommandLineOptions.options);
- }
-
- private static void checkVersion() {
- if(VersionProvider.isLatestMajorVersion() == VersionProvider.VersionStatus.NOT_LATEST) {
- String newDownloadLink = VersionProvider.getDownloadUrlForLatestVersion();
- String newVersionDownloadMessage = newDownloadLink == null ? "\n" :
- "Download the new version (" + VersionProvider.getThisVersion() + ") here:\n" +
- newDownloadLink + "\n";
- System.err.println("\nWARNING: DataSync is out-of-date. " + newVersionDownloadMessage);
- }
- }
-
- // TODO: move the method below to UserPreferences when I get those set of interfaces/classes consolidated.
-
- /**
- * Returns a UserPreferences object which either loads User Prefs from a JSON file
- * or the Java Preferences class (previously saved from GUI mode input)
- *
- * @param cmd
- * @return UserPreferences object containing global preferences
- * @throws IOException
- */
- private static UserPreferences loadUserPreferences(CommandLineOptions options, CommandLine cmd) throws IOException {
- UserPreferences userPrefs;
- if (cmd.getOptionValue(options.CONFIG_FLAG) != null) {
- // load user preferences from given JSON config file
- File configFile = new File(cmd.getOptionValue("config"));
- ObjectMapper mapper = new ObjectMapper();
- userPrefs = mapper.readValue(configFile, UserPreferencesFile.class);
- String proxyUsername = cmd.getOptionValue(options.PROXY_USERNAME_FLAG);
- String proxyPassword = cmd.getOptionValue(options.PROXY_PASSWORD_FLAG);
- String jobType = cmd.getOptionValue(options.JOB_TYPE_FLAG, options.DEFAULT_JOBTYPE);
- if (proxyUsername != null && proxyPassword != null && !jobType.equals(Jobs.LOAD_PREFERENCES_JOB.toString())) {
- userPrefs.setProxyUsername(proxyUsername);
- userPrefs.setProxyPassword(proxyPassword);
- }
- } else {
- // load user preferences from Java preferences class
- userPrefs = new UserPreferencesJava();
- }
- return userPrefs;
- }
-}
+package com.socrata.datasync;
+
+import com.socrata.datasync.job.Job;
+import com.socrata.datasync.job.Jobs;
+import com.socrata.datasync.job.LoadPreferencesJob;
+import com.socrata.datasync.job.PortJob;
+import com.socrata.datasync.job.GISJob;
+import com.socrata.datasync.job.GISJob.ControlDisagreementException;
+import com.socrata.datasync.config.CommandLineOptions;
+import com.socrata.datasync.config.userpreferences.UserPreferences;
+import com.socrata.datasync.config.userpreferences.UserPreferencesFile;
+import com.socrata.datasync.config.userpreferences.UserPreferencesJava;
+import com.socrata.datasync.ui.SimpleIntegrationWizard;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.ParseException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class Main {
+ /**
+ * Loads an instance of the SimpleIntegrationWizard in command line
+ * mode (if arguments are given) or as a GUI (if no arguments are given).
+ */
+ public static void main(String[] args) throws ParseException, ControlDisagreementException {
+ if(args.length == 0) {
+ // Open GUI (default)
+ SimpleIntegrationWizard.get();
+ } else if(args.length == 1) {
+ if (args[0].equals("-?") || args[0].equals("--help")) {
+ printHelp();
+ } else if (args[0].equals("-v") || args[0].equals("--version")) {
+ System.out.println("DataSync version " + VersionProvider.getThisVersion());
+ } else {
+ // Run a job file (.sij) in command-line mode
+ String jobFileToRun = args[0];
+
+ new SimpleIntegrationRunner(jobFileToRun);
+ }
+ } else {
+ // generate & run job from command line args
+ checkVersion();
+
+ CommandLineOptions options = new CommandLineOptions();
+ CommandLine cmd = options.getCommandLine(args);
+ UserPreferences userPrefs = null;
+ try {
+ userPrefs = loadUserPreferences(options, cmd);
+ } catch (IOException e) {
+ System.err.println("Failed to load configuration: " + e.toString());
+ System.exit(1);
+ }
+
+ String jobTypeFlag = options.JOB_TYPE_FLAG;
+ String jobType = cmd.getOptionValue(jobTypeFlag, options.DEFAULT_JOBTYPE);
+
+ Job jobToRun = new com.socrata.datasync.job.IntegrationJob(userPrefs);
+ if(jobType.equals(Jobs.PORT_JOB.toString())) {
+ jobToRun = new PortJob(userPrefs);
+ } else if(jobType.equals(Jobs.GIS_JOB.toString())){
+ jobToRun = new GISJob(userPrefs);
+ } else if(jobType.equals(Jobs.LOAD_PREFERENCES_JOB.toString())) {
+ jobToRun = new LoadPreferencesJob(userPrefs);
+ } else if (!jobType.equals(Jobs.INTEGRATION_JOB.toString())){
+ System.err.println("Invalid " + jobTypeFlag + ": " + cmd.getOptionValue(jobTypeFlag) +
+ " (must be " + Arrays.toString(Jobs.values()) + ")");
+ System.exit(1);
+ }
+
+ if (jobToRun.validateArgs(cmd)) {
+ jobToRun.configure(cmd);
+ new SimpleIntegrationRunner(jobToRun);
+ } else {
+ printHelp();
+ System.exit(1);
+ }
+ }
+ }
+
+ private static void printHelp() {
+ HelpFormatter formatter = new HelpFormatter();
+ formatter.printHelp("DataSync", CommandLineOptions.options);
+ }
+
+ private static void checkVersion() {
+ if(VersionProvider.isLatestMajorVersion() == VersionProvider.VersionStatus.NOT_LATEST) {
+ String newDownloadLink = VersionProvider.getDownloadUrlForLatestVersion();
+ String newVersionDownloadMessage = newDownloadLink == null ? "\n" :
+ "Download the new version (" + VersionProvider.getThisVersion() + ") here:\n" +
+ newDownloadLink + "\n";
+ System.err.println("\nWARNING: DataSync is out-of-date. " + newVersionDownloadMessage);
+ }
+ }
+
+ // TODO: move the method below to UserPreferences when I get those set of interfaces/classes consolidated.
+
+ /**
+ * Returns a UserPreferences object which either loads User Prefs from a JSON file
+ * or the Java Preferences class (previously saved from GUI mode input)
+ *
+ * @param cmd
+ * @return UserPreferences object containing global preferences
+ * @throws IOException
+ */
+ private static UserPreferences loadUserPreferences(CommandLineOptions options, CommandLine cmd) throws IOException {
+ UserPreferences userPrefs;
+ if (cmd.getOptionValue(options.CONFIG_FLAG) != null) {
+ // load user preferences from given JSON config file
+ File configFile = new File(cmd.getOptionValue("config"));
+ ObjectMapper mapper = new ObjectMapper();
+ userPrefs = mapper.readValue(configFile, UserPreferencesFile.class);
+ String proxyUsername = cmd.getOptionValue(options.PROXY_USERNAME_FLAG);
+ String proxyPassword = cmd.getOptionValue(options.PROXY_PASSWORD_FLAG);
+ String jobType = cmd.getOptionValue(options.JOB_TYPE_FLAG, options.DEFAULT_JOBTYPE);
+ if (proxyUsername != null && proxyPassword != null && !jobType.equals(Jobs.LOAD_PREFERENCES_JOB.toString())) {
+ userPrefs.setProxyUsername(proxyUsername);
+ userPrefs.setProxyPassword(proxyPassword);
+ }
+ } else {
+ // load user preferences from Java preferences class
+ userPrefs = new UserPreferencesJava();
+ }
+ return userPrefs;
+ }
+}
diff --git a/src/main/java/com/socrata/datasync/PortUtility.java b/src/main/java/com/socrata/datasync/PortUtility.java
index acfc5bfa..c87121db 100644
--- a/src/main/java/com/socrata/datasync/PortUtility.java
+++ b/src/main/java/com/socrata/datasync/PortUtility.java
@@ -1,5 +1,6 @@
package com.socrata.datasync;
+import com.socrata.api.DatasetDestination;
import com.socrata.api.HttpLowLevel;
import com.socrata.api.Soda2Consumer;
import com.socrata.api.Soda2Producer;
@@ -13,15 +14,17 @@
import com.socrata.model.importer.Dataset;
import com.socrata.model.importer.DatasetInfo;
import com.socrata.model.soql.SoqlQuery;
-import com.sun.jersey.api.client.ClientResponse;
-import org.codehaus.jackson.JsonGenerator;
-import org.codehaus.jackson.map.ObjectMapper;
-import org.codehaus.jackson.type.TypeReference;
+import javax.ws.rs.core.Response;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.core.type.TypeReference;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -36,17 +39,26 @@ private PortUtility() {
public static String portSchema(SodaDdl loader, SodaDdl creator,
final String sourceSetID, final String destinationDatasetTitle,
- final boolean useNewBackend)
+ boolean actuallyCopySchema)
throws SodaError, InterruptedException
{
System.out.print("Copying schema from dataset " + sourceSetID);
Dataset sourceSet = (Dataset) loader.loadDatasetInfo(sourceSetID);
+
if(destinationDatasetTitle != null && !destinationDatasetTitle.equals(""))
sourceSet.setName(destinationDatasetTitle);
- adaptSchemaForAggregates(sourceSet);
+ DatasetDestination destination =
+ sourceSet.isNewBackend() ? DatasetDestination.NBE
+ : DatasetDestination.OBE;
+
+ if(actuallyCopySchema) {
+ adaptSchemaForAggregates(sourceSet);
+ } else {
+ sourceSet.setColumns(Collections.emptyList());
+ }
- DatasetInfo sinkSet = creator.createDataset(sourceSet, useNewBackend);
+ DatasetInfo sinkSet = creator.createDataset(sourceSet, destination);
String sinkSetID = sinkSet.getId();
System.out.println(" to dataset " + sinkSetID);
@@ -86,7 +98,7 @@ private static void upsertContents(Soda2Consumer streamExporter, Soda2Producer s
// Limit of 1000 rows per export, so page through dataset using $offset
int offset = 0;
int rowsUpserted = 0;
- ClientResponse response;
+ Response response;
ObjectMapper mapper = new ObjectMapper();
List