From 8e7b02e61833cdecd143e805a1186719fe0be3b7 Mon Sep 17 00:00:00 2001 From: mitchellsundt Date: Thu, 13 Mar 2014 09:42:35 -0700 Subject: [PATCH 01/13] Add stub project for mysql configuration. Update resteasy jar. Update selenium environment. --- CONFIGURE.txt | 271 +++++++++--------- eclipse-aggregate-gae/.classpath | 10 +- eclipse-aggregate-gae/.gwt/.gwt-log | 3 - .../lib/resteasy-jaxrs-2.3.4.Final.jar | 3 - .../lib/resteasy-jaxrs-3.0.6.Final.jar | 3 + .../war/WEB-INF/lib/selenium-java-2.33.0.jar | 3 - .../war/WEB-INF/lib/selenium-java-2.37.0.jar | 3 + eclipse-aggregate-mysql/.classpath | 6 +- eclipse-aggregate-mysql/SETUP.txt | 16 +- pom.xml | 34 +-- .../aggregate/integration/TestStartPage.java | 52 +++- .../aggregate/integration/TestUploadForm.java | 65 ++++- .../parser/BaseFormParserForJavaRosa.java | 134 ++++----- .../aggregate/servlet/FormUploadServlet.java | 2 +- src/main/libs/readme.txt | 5 +- src/main/resources/common/security.properties | 2 +- 16 files changed, 339 insertions(+), 273 deletions(-) delete mode 100644 eclipse-aggregate-gae/.gwt/.gwt-log delete mode 100644 eclipse-aggregate-gae/war/WEB-INF/lib/resteasy-jaxrs-2.3.4.Final.jar create mode 100644 eclipse-aggregate-gae/war/WEB-INF/lib/resteasy-jaxrs-3.0.6.Final.jar delete mode 100644 eclipse-aggregate-gae/war/WEB-INF/lib/selenium-java-2.33.0.jar create mode 100644 eclipse-aggregate-gae/war/WEB-INF/lib/selenium-java-2.37.0.jar diff --git a/CONFIGURE.txt b/CONFIGURE.txt index 8200f03926..f3f834c8dc 100644 --- a/CONFIGURE.txt +++ b/CONFIGURE.txt @@ -24,9 +24,44 @@ The steps required are: (0) Install the latest Java 6 SDK -(1) Install Eclipse 3.7.2 or higher. This workspace WILL NOT work with +(1a) Install Eclipse 3.7.2 or higher. This workspace WILL NOT work with Eclipse 3.5.2 and earlier. +(1b) Increase the start-up memory sizes for the Eclipse workspace as detailed +below. The GAE database consumes huge amounts of space in the JVM and the GAE +environment runs two independent copies of the server code to mimic the existence +of a foreground and background process. This all requires very large perm space +and max memory for the underlying Java environments. + +To do this, edit the eclipse.ini file, located in the +eclipse directory (e.g., C:\Users\User\eclipse\eclipse.ini). This is +in the same directory as the eclipse.exe. + +This is my eclipse.ini: +------------------------------start------------------- +-startup +plugins/org.eclipse.equinox.launcher_1.2.0.v20110502.jar +--launcher.library +plugins/org.eclipse.equinox.launcher.win32.win32.x86_64_1.1.100.v20110502 +-product +org.eclipse.epp.package.jee.product +--launcher.defaultAction +openFile +--launcher.XXMaxPermSize +1536M +-showsplash +org.eclipse.platform +--launcher.XXMaxPermSize +1536m +--launcher.defaultAction +openFile +-vmargs +-Dosgi.requiredJavaVersion=1.6 +-Xms100m +-Xmx2048m +-startup +------------------------------end---------------------- + (2) Install Google Eclipse Plugin with App Engine SDK and Google Web Toolkit SDK. (3) Start Eclipse. Choose the parent directory of the clone'd repository as your workspace. @@ -41,11 +76,11 @@ Windows / Preferences: Google / Web Toolkit: - Be sure there is a WebToolkit SDK defined 2.5.0 or higher. + Be sure there is a WebToolkit SDK defined 2.5.1 or higher. The default GWT version should match that in the maven - pom.xml (currently 2.5.0). The GWT library is installed as + pom.xml (currently 2.5.1). The GWT library is installed as part of the plugin and can be found in: - ...\eclipse\plugins\com.google.gwt.eclipse.sdkbundle_2.5.0...\gwt-2.5.0 + ...\eclipse\plugins\com.google.gwt.eclipse.sdkbundle_2.5.1...\gwt-2.5.1 File / Import: @@ -63,7 +98,7 @@ In this file: Set the security.server.superUser to your gmail account. Additionally, set the security.server.hostname= -(blank) or to a dynamic DNS name or IP for your development box. +(blank) to a dynamic DNS name or IP for your development box. Save. @@ -82,10 +117,13 @@ Select eclipse-aggregate-gae right-click Properties under /Builders + de-select Enhancer (based on the bug: http://code.google.com/p/googleappengine/issues/detail?id=1970 ) under /Google/App Engine -toggle the App Engine SDK between the default and specific settings (to make a rebuild as per instructions http://code.google.com/p/opendatakit/wiki/EclipseDebugging ) + +toggle the App Engine SDK between the default and specific settings (to make a +rebuild as per instructions http://code.google.com/p/opendatakit/wiki/EclipseDebugging ) (7) Select eclipse-aggregate-gae project. @@ -98,17 +136,77 @@ to this, it is located under: Package: org.opendatakit.aggregate Filename: AggregateUI.gwt.xml -(8) Running: +Click on 'Advanced' to open more options. + +In the 'VM Arguments' section, add: + +-Xmx1536m + +Click 'Compile' + +This should now compile. + +================================== +Running or Debugging under Eclipse +================================== Select eclipse-aggregate-gae project. -Run As 'Web Application' +The first time you run or debug, you need to increase the JVM sizes + +Run As 'Run Configurations...' +Debug As 'Debug Configurations...' + +Choose 'Web Application' + +Open this up, and select 'index.html' + +On the '(X)= Arguments' tab, insert the following at the front of the 'VM Arguments' section: + +-Xmx2048m -XX:MaxPermSize=1536m + +Click 'Run' or 'Debug' + +This should start up the server. + +GWT UI debugging needs to be done on a Chrome browser. + +================================== +Debugging GWT UI in Chrome Browser +================================== + +Open a Chrome browser + +Go to Tools / Extensions + +Choose to 'Get more extensions' + +Search for 'GWT Developer Plugin' and install it. + +Ensure that it is enabled. -Choose index.html +Now paste in the URL from Eclipse's 'Development Mode' tab into +your browser. This will typically be: -Double-click to open the url. After logging on, if running with -the debug server, you will need to hand-edit the url to restore -the gwt-server address. +http://127.0.0.1:8888/index.html?gwt-server=127.0.0.1:9997 + +This will redirect to the login screen. After logging in +you will then be redirected to the Aggregate.html page and the +main UI. + +At this point, the query string, ?gwt-server=127.0.0.1:9997 +may drop off. This will disconnect the UI rendering from the +client Java code, causing the rendering to fall back to the +last manually compiled GWT code. Be on the alert for this. +If it happens, just add the query string back into the URL +before the hash (#) mark. + +The URL should show have a red GWT toolbox to its right. If this +ever changes to gray, you need to click on the toolbox and allow +the new hostname and port to access GWT. If you specify an IP address +or hostname in the security.properties file, you generally need to +do this (you would need to do that in order for a device to reach +your server). ===================== MySQL Eclipse Setup @@ -178,7 +276,7 @@ To delete the local datastore: (3) delete "local_db.bin" ========================================== -Full Development Environment Configuration +Full Maven Development Environment Configuration ------------------------------------------ (1) Install Maven 3. This will generally set up a maven repository under @@ -241,8 +339,14 @@ See the readme there for the maven commands to upload those files into your local repository. In some cases, these are cutting edge Google libraries that may no longer need to be manually installed (once they get published to maven). +(8a) Download and install Firefox ESR 24. This is a slowly-upgrading version +of Firefox, since the upgrade cycle of Firefox is too rapid for Selenium +(our UI testing framework) to keep up. + (8) Download the Java Selenium Client Driver ( http://seleniumhq.org/download/ ) -The Maven pom.xml expects version 2.33.0. Explode the zip and make note of +Since we use Firefox ESR 24, we need to use 2.37, available here: +https://code.google.com/p/selenium/downloads/detail?name=selenium-java-2.37.0.zip +The Maven pom.xml expects version 2.37.0. Explode the zip and make note of the directory. (9) Edit Maven's settings.xml file (this is in the .m2 directory). @@ -275,8 +379,8 @@ A minimal file is: MYSQLROOTPASSWORDHERE C:\\Program Files\\PostgreSQL\\9.1\\bin\\psql.exe POSTGRESQLROOTPASSWORDHERE - C:\\Users\\Administrator\\Documents\\selenium\\selenium-2.33.0 - C:\\Users\\Administrator\\Documents\\selenium\\selenium-2.33.0\\libs + C:\\Users\\Administrator\\Documents\\selenium\\selenium-2.37.0 + C:\\Users\\Administrator\\Documents\\selenium\\selenium-2.37.0\\libs YOUR.FULLY.QUALIFIED.HOSTNAME.AND.ORG 7070 7443 @@ -297,123 +401,18 @@ etc. and do not need bitrock installed. The aggregate-mysql war file is used as the starting point for the installer build process. ----------- - -(10) Start Eclipse. Choose the parent directory of the clone'd repository as your workspace. - -Windows / Preferences: - - Google / App Engine: - - Be sure there is an App Engine 1.8.0 or higher SDK defined. - The default GWT version should match that in the maven - pom.xml (currently 1.8.0). - - Google / Web Toolkit: - - Be sure there is a WebToolkit SDK defined 2.5.0 or higher. - The default GWT version should match that in the maven - pom.xml (currently 2.5.0). - - Java / Build Path / Classpath Variables: - - Define a new variable, M2_REPO, pointing to the .m2/repository - directory, or wherever you have configured your local - Maven repository to be. - - Java / Installed JREs: - - Add the JDK path. (needed only for Maven-based builds) - - Java / Installed JREs / Execution Environments: - - Choose JavaSE-1.6, check the checkbox beside the JDK. (needed only for Maven-based builds). - - Server / Runtime Environments: +========================= +Maven Command Line Builds +========================= - Add: Apache Tomcat 6.0 and point it to the install location - used in (3) earlier. This is optional if you will not be - importing or using the MySQL and Postgres builds. +(12) Maven command-line builds are done as follows: - File / Import: +mvn clean - Choose the directory of the clone'd repository as the directory to search - (this is one level below the workspace directory). - - Choose ONLY the eclipse-aggregate-gae and odk-gae-settings projects. - - This should refresh and build without any errors. - -(11) Exit eclipse. Then edit the eclipse.ini file, located in the -eclipse directory (e.g., C:\Users\User\eclipse\eclipse.ini). This is -in the same directory as the eclipse.exe. - -You need to increase the Perm space of the JVM and the overall max size -of the JVM. The key entries are the XXMaxPermSzie 512m and the -Xmx1024m. - -This is my eclipse.ini: -------------------------------start------------------- --startup -plugins/org.eclipse.equinox.launcher_1.2.0.v20110502.jar ---launcher.library -plugins/org.eclipse.equinox.launcher.win32.win32.x86_64_1.1.100.v20110502 --product -org.eclipse.epp.package.jee.product ---launcher.defaultAction -openFile ---launcher.XXMaxPermSize -512M --showsplash -org.eclipse.platform ---launcher.XXMaxPermSize -512m ---launcher.defaultAction -openFile --vmargs --Dosgi.requiredJavaVersion=1.5 --Xms100m --Xmx1024m --startup -------------------------------end---------------------- - -(12) If you want to experiment with building Maven inside Eclipse, -install the m2e Eclipse plugin http://www.eclipse.org/m2e/ . The -Maven pom.xml files have been modified to provide missing lifecycle -definitions for, e.g., ant. You will also need to install the -"m2e connector for build-helper-maven-plugin" - -Support for this is still dodgy within Eclipse so for now, we use -maven for command-line builds and Eclipse gae for development. - -(13) Edit odk-gae-settings / common / security.properties - -Be sure to set the security.server.superUser to your gmail account. Save. - -(14) Select the 'build.xml' (Ant Buildfile within odk-gae-settings), right-click, and -choose Run As / Ant Build. This will rebuild the configuration -jar and copy it into the eclipse project. - -(15) Refresh the eclipse-aggregate-gae project (to pick up the updated jar file). - -Select eclipse-aggregate-gae project. - -Click on the red toolbox (GWT compile) to compile the GWT Java code into javascript. - -(15) Running: - -Select eclipse-aggregate-gae project. - -Run As 'Web Application' - -Choose index.html - -Double-click to open the url. After logging on, if running with -the debug server, you will need to hand-edit the url to restore -the gwt-server address. - -(16) For the non-gae projects, you will need to first build -with maven manually. Open a command window, browse to the -clone'd repository directroy. Type: +This cleans the workspace, removing all temporary files. +If this errors out, verify that there are no orphaned java +executables running. If the GAE tests crash, they can leave +a java database background process running. mvn install @@ -421,16 +420,16 @@ This will build and install the projects, running the unit tests against the 3 datastores (Google BigTable, MySQL, Postgresql), and building the wars for the 3 platforms. -(17) you can now also import the aggregate-mysql project into -eclipse. It can be used to export a WAR file similar to what -can be built with Maven. - -(18) If you have bitrock installed and licensed, you can +(13) If you have bitrock installed and licensed, you can build the bitrock installer. First, copy aggregate-mysql\target\aggregate-mysql-1.0.war bitrock-installer cd bitrock-installer mvn clean mvn install Open bitrock and open the buildWar.xml project file in this directory. -On the packaging page, build for windows, linux and OSX. +On the packaging page, build for windows, linux, linux-64 and OSX. + +On Windows, the generated installers are placed under: + +C:\Users\Administrator\Documents\InstallBuilder\output diff --git a/eclipse-aggregate-gae/.classpath b/eclipse-aggregate-gae/.classpath index a32a3a378b..bc76a67da0 100644 --- a/eclipse-aggregate-gae/.classpath +++ b/eclipse-aggregate-gae/.classpath @@ -1,7 +1,7 @@ - + @@ -44,8 +44,8 @@ - + @@ -59,7 +59,7 @@ - + @@ -102,7 +102,7 @@ - - + + diff --git a/eclipse-aggregate-gae/.gwt/.gwt-log b/eclipse-aggregate-gae/.gwt/.gwt-log deleted file mode 100644 index 717079410c..0000000000 --- a/eclipse-aggregate-gae/.gwt/.gwt-log +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ba045aa762ac5d026c5d53148ea0c12e55eb46ec814dcadc60b330ee74b7fea0 -size 1517330 diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/resteasy-jaxrs-2.3.4.Final.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/resteasy-jaxrs-2.3.4.Final.jar deleted file mode 100644 index f9f6f7d16d..0000000000 --- a/eclipse-aggregate-gae/war/WEB-INF/lib/resteasy-jaxrs-2.3.4.Final.jar +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:09f3055d265c18ffccbe2462addb0d8baea2d22b7425f0c1a9da2da9f72b77c3 -size 615467 diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/resteasy-jaxrs-3.0.6.Final.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/resteasy-jaxrs-3.0.6.Final.jar new file mode 100644 index 0000000000..d8243daa2d --- /dev/null +++ b/eclipse-aggregate-gae/war/WEB-INF/lib/resteasy-jaxrs-3.0.6.Final.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:073259ce28099a748692f63e60b8d82a8607233675e8941ffcdcd611a0138915 +size 824548 diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/selenium-java-2.33.0.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/selenium-java-2.33.0.jar deleted file mode 100644 index 8190724bd3..0000000000 --- a/eclipse-aggregate-gae/war/WEB-INF/lib/selenium-java-2.33.0.jar +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4a2c9d554f84301ede4cdb9acce5576354a82a91a20b386afbe8807291899298 -size 3801559 diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/selenium-java-2.37.0.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/selenium-java-2.37.0.jar new file mode 100644 index 0000000000..6ce4035cb4 --- /dev/null +++ b/eclipse-aggregate-gae/war/WEB-INF/lib/selenium-java-2.37.0.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b6f41b889df7c4fda59da8cc9c56f961f2697277b69c724ac17236e0d6c358bd +size 3940807 diff --git a/eclipse-aggregate-mysql/.classpath b/eclipse-aggregate-mysql/.classpath index 11d798e0fc..45e84ffab4 100644 --- a/eclipse-aggregate-mysql/.classpath +++ b/eclipse-aggregate-mysql/.classpath @@ -44,8 +44,8 @@ - + @@ -59,7 +59,7 @@ - + @@ -96,7 +96,7 @@ - + diff --git a/eclipse-aggregate-mysql/SETUP.txt b/eclipse-aggregate-mysql/SETUP.txt index 5495fb4530..290d5603c2 100644 --- a/eclipse-aggregate-mysql/SETUP.txt +++ b/eclipse-aggregate-mysql/SETUP.txt @@ -1,6 +1,8 @@ To set up a MySQL development version, do the following: -(1) copy the war directory of eclipse-aggregate-gae to this directory. +(0) get the eclipse-aggregate-gae project built and runnable. + +(1) copy the war directory of eclipse-aggregate-gae into this directory. (2) delete the file: @@ -21,3 +23,15 @@ You should now be able to run on port 8080 of that new server. It is apparently normal for the minimized jquery Javascript to fail validation. You can ignore that. +============================= +Issues +------ + +The server seems to have a hard time getting source code changes +published to it. A solution that works is to: + +Stop the server + +Clean... the server + +Start the server diff --git a/pom.xml b/pom.xml index d5686b3dc7..06a09db293 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ 1.16.0-rc v2-rev91-1.16.0-rc 2.5.1 - 2.3.4.Final + 3.0.6.Final 1.2 2.12.4 2.12.4 @@ -39,15 +39,15 @@ 2.3 1.7 3.0 - 4.10 - 1.9.11 + 4.11 + 1.9.12 3.1.3.RELEASE 1.6.6 3.1.3.odk-SNAPSHOT 0.9.6.662.odk-SNAPSHOT 3.1.3.odk-SNAPSHOT - 2.33.0 + 2.37.0 - 2013-09-30 + 2014-03-07 jar compile @@ -1258,7 +1258,7 @@ test - ${selenium.home}/selenium-java-2.33.0.jar + ${selenium.home}/selenium-java-2.37.0.jar ${selenium.libs}/apache-mime4j-0.6.jar ${selenium.libs}/bsh-1.3.0.jar ${selenium.libs}/cglib-nodep-2.1_3.jar @@ -1269,15 +1269,15 @@ ${selenium.libs}/commons-jxpath-1.3.jar ${selenium.libs}/commons-lang3-3.1.jar ${selenium.libs}/commons-logging-1.1.1.jar - ${selenium.libs}/cssparser-0.9.9.jar - ${selenium.libs}/guava-14.0.jar + ${selenium.libs}/cssparser-0.9.11.jar + ${selenium.libs}/guava-15.0.jar ${selenium.libs}/hamcrest-core-1.3.jar ${selenium.libs}/hamcrest-library-1.3.jar - ${selenium.libs}/htmlunit-2.12.jar - ${selenium.libs}/htmlunit-core-js-2.12.jar - ${selenium.libs}/httpclient-4.2.1.jar - ${selenium.libs}/httpcore-4.2.1.jar - ${selenium.libs}/httpmime-4.2.1.jar + ${selenium.libs}/htmlunit-2.13.jar + ${selenium.libs}/htmlunit-core-js-2.13.jar + ${selenium.libs}/httpclient-4.3.1.jar + ${selenium.libs}/httpcore-4.3.jar + ${selenium.libs}/httpmime-4.3.1.jar ${selenium.libs}/ini4j-0.5.2.jar ${selenium.libs}/jcommander-1.29.jar ${selenium.libs}/jetty-websocket-8.1.8.jar @@ -1286,14 +1286,14 @@ ${selenium.libs}/json-20080701.jar ${selenium.libs}/junit-dep-4.11.jar ${localRepository}/junit/junit/${junit.version}/junit-${junit.version}.jar - ${selenium.libs}/nekohtml-1.9.17.jar + ${selenium.libs}/nekohtml-1.9.19.jar ${selenium.libs}/netty-3.5.7.Final.jar - ${selenium.libs}/operadriver-1.3.jar - ${selenium.libs}/phantomjsdriver-1.0.3.jar + ${selenium.libs}/operadriver-1.5.jar + ${selenium.libs}/phantomjsdriver-1.0.4.jar ${selenium.libs}/protobuf-java-2.4.1.jar ${selenium.libs}/sac-1.3.jar ${selenium.libs}/serializer-2.7.1.jar - ${selenium.libs}/testing-6.8.jar + ${selenium.libs}/testing-6.8.5.jar ${selenium.libs}/xalan-2.7.1.jar ${selenium.libs}/xercesImpl-2.10.0.jar ${selenium.libs}/xml-apis-1.4.01.jar diff --git a/src/it/java/org/opendatakit/aggregate/integration/TestStartPage.java b/src/it/java/org/opendatakit/aggregate/integration/TestStartPage.java index 90195cf8d3..c6eee4c0c5 100644 --- a/src/it/java/org/opendatakit/aggregate/integration/TestStartPage.java +++ b/src/it/java/org/opendatakit/aggregate/integration/TestStartPage.java @@ -4,15 +4,15 @@ import java.util.concurrent.TimeUnit; +import java.util.List; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; -import org.openqa.selenium.WebDriverBackedSelenium; +import org.openqa.selenium.WebElement; import org.openqa.selenium.firefox.FirefoxDriver; import org.openqa.selenium.firefox.FirefoxProfile; -import com.thoughtworks.selenium.Selenium; import com.thoughtworks.selenium.Wait; @RunWith(org.junit.runners.JUnit4.class) @@ -27,7 +27,7 @@ public class TestStartPage { private static String password = "aggregate"; private static int port; private static FirefoxDriver driver; - private static Selenium selenium; + private static String fullRootUrl; @BeforeClass public static void setUp() throws Exception { @@ -35,46 +35,76 @@ public static void setUp() throws Exception { hostname = System.getProperty("test.server.hostname"); baseUrl = System.getProperty("test.server.baseUrl"); port = Integer.parseInt(System.getProperty("test.server.port")); + fullRootUrl = "http://" + username + ":" + password + "@" + hostname + ":" + port + baseUrl; // We should also test different browsers? FirefoxProfile profile = new FirefoxProfile(); + profile.setEnableNativeEvents(false); profile.setPreference("network.negotiate-auth.trusteduris", hostname); driver = new FirefoxDriver(profile); driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS); - selenium = new WebDriverBackedSelenium(driver, "http://" + username + ":" + password + "@" - + hostname + ":" + port + baseUrl); + driver.get(fullRootUrl); - selenium.open("local_login.html"); + driver.get(fullRootUrl + "local_login.html"); + + // wait for login process to complete... try { Thread.sleep(7000); } catch (Exception e) { } + // and verify that the Form Management tab appears... Wait mainload = new Wait() { public boolean until() { - return selenium.isTextPresent("Form Management"); + try { + List elements = driver.findElementsByClassName("gwt-Label"); + for (WebElement e : elements) { + if (e.getText().equals("Form Management")) + return true; + } + } catch (Exception e) { + e.printStackTrace(); + } + return false; } }; + mainload.wait("Login did not progress to Aggregate.html page", TIMEOUT_INTERVAL_MS, RETRY_INTERVAL_MS); } @Test public void testStartPageHasCorrectTitle() throws Exception { - selenium.open("Aggregate.html"); + driver.get(fullRootUrl + "Aggregate.html"); + + // wait for login process to complete... + try { + Thread.sleep(7000); + } catch (Exception e) { + } + Wait mainload = new Wait() { public boolean until() { - return selenium.isTextPresent("Form Management"); + try { + List elements = driver.findElementsByClassName("gwt-Label"); + for (WebElement e : elements) { + if (e.getText().equals("Form Management")) + return true; + } + } catch (Exception e) { + e.printStackTrace(); + } + return false; } }; mainload.wait("Login did not progress to Aggregate.html page", TIMEOUT_INTERVAL_MS, RETRY_INTERVAL_MS); - assertEquals("ODK Aggregate", selenium.getTitle()); + assertEquals("ODK Aggregate", driver.getTitle()); // assertEquals("Name", selenium.getText("//th[1]")); // assertTrue(selenium.isElementPresent("link=List Forms")); } @AfterClass public static void tearDown() throws Exception { - selenium.close(); + driver.close(); } } diff --git a/src/it/java/org/opendatakit/aggregate/integration/TestUploadForm.java b/src/it/java/org/opendatakit/aggregate/integration/TestUploadForm.java index 0d29218a2a..6e71fef199 100644 --- a/src/it/java/org/opendatakit/aggregate/integration/TestUploadForm.java +++ b/src/it/java/org/opendatakit/aggregate/integration/TestUploadForm.java @@ -1,17 +1,17 @@ package org.opendatakit.aggregate.integration; import java.io.File; +import java.util.List; import java.util.concurrent.TimeUnit; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; -import org.openqa.selenium.WebDriverBackedSelenium; +import org.openqa.selenium.WebElement; import org.openqa.selenium.firefox.FirefoxDriver; import org.openqa.selenium.firefox.FirefoxProfile; -import com.thoughtworks.selenium.Selenium; import com.thoughtworks.selenium.Wait; @RunWith(org.junit.runners.JUnit4.class) @@ -26,7 +26,7 @@ public class TestUploadForm { private static String password = "aggregate"; private static int port; private static FirefoxDriver driver; - private static Selenium selenium; + private static String fullRootUrl; @BeforeClass public static void setUp() throws Exception { @@ -34,20 +34,24 @@ public static void setUp() throws Exception { hostname = System.getProperty("test.server.hostname"); baseUrl = System.getProperty("test.server.baseUrl"); port = Integer.parseInt(System.getProperty("test.server.port")); + fullRootUrl = "http://" + username + ":" + password + "@" + hostname + ":" + port + baseUrl; // We should also test different browsers? FirefoxProfile profile = new FirefoxProfile(); + profile.setEnableNativeEvents(false); profile.setPreference("network.negotiate-auth.trusteduris", hostname); driver = new FirefoxDriver(profile); - driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS); - selenium = new WebDriverBackedSelenium(driver, "http://" + username + ":" + password + "@" - + hostname + ":" + port + baseUrl); + driver.manage().timeouts().implicitlyWait(TIMEOUT_INTERVAL_MS, TimeUnit.MILLISECONDS); + driver.get(fullRootUrl); System.out.println(formsDir); System.out.println(hostname); System.out.println(baseUrl); System.out.println(port + ""); - selenium.open("local_login.html"); + // this may not work... + driver.get(fullRootUrl + "local_login.html"); + + // wait for login process to complete... try { Thread.sleep(7000); } catch (Exception e) { @@ -55,7 +59,16 @@ public static void setUp() throws Exception { Wait mainload = new Wait() { public boolean until() { - return selenium.isTextPresent("Form Management"); + try { + List elements = driver.findElementsByClassName("gwt-Label"); + for (WebElement e : elements) { + if (e.getText().equals("Form Management")) + return true; + } + } catch (Exception e) { + e.printStackTrace(); + } + return false; } }; mainload.wait("Login did not progress to Aggregate.html page", TIMEOUT_INTERVAL_MS, @@ -64,19 +77,43 @@ public boolean until() { @Test public void testUploadForm() throws Exception { - selenium.open("upload"); + driver.get(fullRootUrl + "upload"); Wait newload = new Wait() { public boolean until() { - return selenium.isTextPresent("Upload one form into ODK Aggregate"); + try { + List elements = driver.findElementsByTagName("h2"); + for (WebElement e : elements) { + if (e.getText().equals("Upload one form into ODK Aggregate")) + return true; + } + } catch (Exception e) { + e.printStackTrace(); + } + return false; } }; newload.wait("Upload page did not render", TIMEOUT_INTERVAL_MS, RETRY_INTERVAL_MS); File form = new File(formsDir + "/landUse.xml"); - selenium.type("form_def_file", form.getCanonicalPath()); - selenium.click("//input[@value='Upload Form']"); + driver.findElementById("form_def_file").sendKeys(form.getCanonicalPath()); + WebElement theUploadButton = driver.findElementById("upload_form"); + if (theUploadButton == null) { + throw new IllegalStateException("could not find the upload button"); + } + theUploadButton.submit(); + Thread.sleep(1000); + // and wait for it to reload Wait reload = new Wait() { public boolean until() { - return selenium.isTextPresent("Successful form upload."); + try { + List ps = driver.findElementsByTagName("p"); + for (WebElement e : ps) { + if (e.getText().equals("Successful form upload.")) + return true; + } + } catch (Exception e) { + e.printStackTrace(); + } + return false; } }; reload.wait("Upload was not successful or did not return", TIMEOUT_INTERVAL_MS, @@ -86,6 +123,6 @@ public boolean until() { @AfterClass public static void tearDown() throws Exception { - selenium.close(); + driver.close(); } } diff --git a/src/main/java/org/opendatakit/aggregate/parser/BaseFormParserForJavaRosa.java b/src/main/java/org/opendatakit/aggregate/parser/BaseFormParserForJavaRosa.java index 2334d09679..232a7c70ad 100644 --- a/src/main/java/org/opendatakit/aggregate/parser/BaseFormParserForJavaRosa.java +++ b/src/main/java/org/opendatakit/aggregate/parser/BaseFormParserForJavaRosa.java @@ -17,6 +17,7 @@ package org.opendatakit.aggregate.parser; +import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; import java.util.Arrays; @@ -33,6 +34,7 @@ import org.javarosa.core.model.FormDef; import org.javarosa.core.model.IDataReference; import org.javarosa.core.model.SubmissionProfile; +import org.javarosa.core.model.instance.AbstractTreeElement; import org.javarosa.core.model.instance.FormInstance; import org.javarosa.core.model.instance.TreeElement; import org.javarosa.core.model.instance.TreeReference; @@ -66,28 +68,32 @@ public class BaseFormParserForJavaRosa { * Classes needed to serialize objects. Need to put anything from JR in here. */ public final static String[] SERIALIABLE_CLASSES = { - "org.javarosa.core.services.locale.ResourceFileDataSource", // JavaRosaCoreModule - "org.javarosa.core.services.locale.TableLocaleSource", // JavaRosaCoreModule - "org.javarosa.core.model.FormDef", - "org.javarosa.core.model.SubmissionProfile", // CoreModelModule - "org.javarosa.core.model.QuestionDef", // CoreModelModule - "org.javarosa.core.model.GroupDef", // CoreModelModule - "org.javarosa.core.model.instance.FormInstance", // CoreModelModule - "org.javarosa.core.model.data.BooleanData", // CoreModelModule - "org.javarosa.core.model.data.DateData", // CoreModelModule - "org.javarosa.core.model.data.DateTimeData", // CoreModelModule - "org.javarosa.core.model.data.DecimalData", // CoreModelModule - "org.javarosa.core.model.data.GeoPointData", // CoreModelModule - "org.javarosa.core.model.data.IntegerData", // CoreModelModule - "org.javarosa.core.model.data.LongData", // CoreModelModule - "org.javarosa.core.model.data.MultiPointerAnswerData", // CoreModelModule - "org.javarosa.core.model.data.PointerAnswerData", // CoreModelModule - "org.javarosa.core.model.data.SelectMultiData", // CoreModelModule - "org.javarosa.core.model.data.SelectOneData", // CoreModelModule - "org.javarosa.core.model.data.StringData", // CoreModelModule - "org.javarosa.core.model.data.TimeData", // CoreModelModule - "org.javarosa.core.model.data.UncastData", // CoreModelModule - "org.javarosa.core.model.data.helper.BasicDataPointer" // CoreModelModule + "org.javarosa.core.services.locale.ResourceFileDataSource", // JavaRosaCoreModule + "org.javarosa.core.services.locale.TableLocaleSource", // JavaRosaCoreModule + "org.javarosa.core.model.FormDef", + "org.javarosa.core.model.SubmissionProfile", // CoreModelModule + "org.javarosa.core.model.QuestionDef", // CoreModelModule + "org.javarosa.core.model.GroupDef", // CoreModelModule + "org.javarosa.core.model.instance.FormInstance", // CoreModelModule + "org.javarosa.core.model.data.BooleanData", // CoreModelModule + "org.javarosa.core.model.data.DateData", // CoreModelModule + "org.javarosa.core.model.data.DateTimeData", // CoreModelModule + "org.javarosa.core.model.data.DecimalData", // CoreModelModule + "org.javarosa.core.model.data.GeoLineData", // CoreModelModule + "org.javarosa.core.model.data.GeoPointData", // CoreModelModule + "org.javarosa.core.model.data.GeoShapeData", // CoreModelModule + "org.javarosa.core.model.data.IntegerData", // CoreModelModule + "org.javarosa.core.model.data.LongData", // CoreModelModule + "org.javarosa.core.model.data.MultiPointerAnswerData", // CoreModelModule + "org.javarosa.core.model.data.PointerAnswerData", // CoreModelModule + "org.javarosa.core.model.data.SelectMultiData", // CoreModelModule + "org.javarosa.core.model.data.SelectOneData", // CoreModelModule + "org.javarosa.core.model.data.StringData", // CoreModelModule + "org.javarosa.core.model.data.TimeData", // CoreModelModule + "org.javarosa.core.model.data.UncastData", // CoreModelModule + "org.javarosa.core.model.data.helper.BasicDataPointer", // CoreModelModule + "org.javarosa.core.model.Action", // CoreModelModule + "org.javarosa.core.model.actions.SetValueAction" //CoreModelModule }; private static boolean isJavaRosaInitialized = false; @@ -115,53 +121,35 @@ private static final void initializeJavaRosa() { private static final String BASE64_ENCRYPTED_FIELD_KEY = "base64EncryptedFieldKey"; private static final String BASE64_RSA_PUBLIC_KEY = "base64RsaPublicKey"; - public static enum DifferenceResult { // result from comparing two XForms + // result from comparing two XForms + public static enum DifferenceResult { XFORMS_IDENTICAL, // instance and body are identical XFORMS_SHARE_INSTANCE, // instances (including binding) identical; body // differs XFORMS_SHARE_SCHEMA, // instances differ, but share common database schema XFORMS_DIFFERENT, // instances differ significantly enough to affect - // database - // schema + // database schema XFORMS_MISSING_VERSION, XFORMS_EARLIER_VERSION } - private static final String[] ChangeableBindAttributes = { // bind attributes - // that CAN change - // without - // affecting - // database - // structure + // bind attributes that CAN change without affecting database structure + // Note: must specify these entirely in lowercase + private static final String[] ChangeableBindAttributes = { "relevant", "constraint", "readonly", "required", "calculate", XFormParser.NAMESPACE_JAVAROSA.toLowerCase() + ":constraintmsg", XFormParser.NAMESPACE_JAVAROSA.toLowerCase() + ":preload", - XFormParser.NAMESPACE_JAVAROSA.toLowerCase() + ":preloadparams", "appearance" }; // Note: - // must - // specify - // the - // above - // in - // all - // lowercase - - private static final String[] NonchangeableInstanceAttributes = { // core - // instance - // def. - // attrs. - // that - // CANNOT - // change - // w/o - // affecting - // db - // structure - "id" }; // Note: must specify the above in all lowercase - - private static final String NODESET_ATTR = "nodeset"; // nodeset attribute - // name, in - // elements - private static final String TYPE_ATTR = "type"; // type attribute name, in - // elements + XFormParser.NAMESPACE_JAVAROSA.toLowerCase() + ":preloadparams", "appearance" }; + + // core instance def. attrs. that CANNOT change w/o affecting db structure + // Note: must specify these entirely in lowercase + private static final String[] NonchangeableInstanceAttributes = { + "id" }; + + // nodeset attribute name, in elements + private static final String NODESET_ATTR = "nodeset"; + + // type attribute name, in elements + private static final String TYPE_ATTR = "type"; private static final String ENCRYPTED_FORM_DEFINITION = "" + " path = new ArrayList(); while (e != null && e.getName() != null) { path.add(e.getName()); @@ -639,7 +625,11 @@ protected BaseFormParserForJavaRosa(String existingXml, String existingTitle, bo // the // storage for this form. XFormParserWithBindEnhancements exfp = parseFormDefinition(ENCRYPTED_FORM_DEFINITION, this); - formDef = exfp.parse(); + try { + formDef = exfp.parse(); + } catch (IOException e) { + throw new ODKIncompleteSubmissionData("Exception " + e.toString() + " during parsing!", Reason.BAD_JR_PARSE); + } if (formDef == null) { throw new ODKIncompleteSubmissionData("Javarosa failed to construct Encrypted FormDef!", @@ -670,11 +660,11 @@ protected BaseFormParserForJavaRosa(String existingXml, String existingTitle, bo @SuppressWarnings("unused") private void printTreeElementInfo(TreeElement treeElement) { - System.out.println("processing te: " + treeElement.getName() + " type: " + treeElement.dataType - + " repeatable: " + treeElement.repeatable); + System.out.println("processing te: " + treeElement.getName() + " type: " + treeElement.getDataType() + + " repeatable: " + treeElement.isRepeatable()); } - public String getTreeElementPath(TreeElement e) { + public String getTreeElementPath(AbstractTreeElement e) { if (e == null) return null; String s = getTreeElementPath(e.getParent()); @@ -803,9 +793,9 @@ public static DifferenceResult compareXml(BaseFormParserForJavaRosa incomingPars // that will be submitted to Aggregate. IDataReference r; r = subProfile1.getRef(); - TreeElement e1 = (r != null) ? formDef1.getInstance().resolveReference(r) : null; + AbstractTreeElement e1 = (r != null) ? formDef1.getInstance().resolveReference(r) : null; r = subProfile2.getRef(); - TreeElement e2 = (r != null) ? formDef2.getInstance().resolveReference(r) : null; + AbstractTreeElement e2 = (r != null) ? formDef2.getInstance().resolveReference(r) : null; if (e1 != null && e2 != null) { // both return only a portion of the form. @@ -1024,15 +1014,14 @@ public static DifferenceResult compareTreeElements(TreeElement treeElement1, // Otherwise, if you have specified the jr:template attribute, // it appears only as an INDEX_TEMPLATE element. - @SuppressWarnings("unused") int template1DropCount = 0; // get non-template entries for treeElement1 List element1ExcludingRepeatIndex0Children = new ArrayList(); for (int i = 0; i < treeElement1.getNumChildren(); i++) { TreeElement child = (TreeElement) treeElement1.getChildAt(i); - if (child.repeatable) { - if (child.multiplicity != TreeReference.INDEX_TEMPLATE) { + if (child.isRepeatable()) { + if (child.getMult() != TreeReference.INDEX_TEMPLATE) { template1DropCount++; log.info("element1:dropping " + child.getName()); continue; @@ -1042,15 +1031,14 @@ public static DifferenceResult compareTreeElements(TreeElement treeElement1, element1ExcludingRepeatIndex0Children.add(child); } - @SuppressWarnings("unused") int template2DropCount = 0; // get non-template entries for treeElement2 Map element2ExcludingRepeatIndex0Children = new HashMap(); for (int i = 0; i < treeElement2.getNumChildren(); i++) { TreeElement child = (TreeElement) treeElement2.getChildAt(i); - if (child.repeatable) { - if (child.multiplicity != TreeReference.INDEX_TEMPLATE) { + if (child.isRepeatable()) { + if (child.getMult() != TreeReference.INDEX_TEMPLATE) { template2DropCount++; log.info("element2:dropping " + child.getName()); continue; diff --git a/src/main/java/org/opendatakit/aggregate/servlet/FormUploadServlet.java b/src/main/java/org/opendatakit/aggregate/servlet/FormUploadServlet.java index 3a09cdc9e9..1a612757e6 100644 --- a/src/main/java/org/opendatakit/aggregate/servlet/FormUploadServlet.java +++ b/src/main/java/org/opendatakit/aggregate/servlet/FormUploadServlet.java @@ -112,7 +112,7 @@ public class FormUploadServlet extends ServletUtilBase { + " " + " \n" + " " - + " " + + " " + " " + " " + " \n" diff --git a/src/main/libs/readme.txt b/src/main/libs/readme.txt index 4a440bbe7e..8b2d1ad9cc 100644 --- a/src/main/libs/readme.txt +++ b/src/main/libs/readme.txt @@ -4,9 +4,10 @@ They need to be installed into the local M2_REPO repository. javarosa-libraries: This is a special build of javarosa using the tree at https://bitbucket.org/m.sundt/javarosa -It incorporates multithread-safe KoBo collect changes (from Clayton), round(a,d) function and saving and restoring of bind attributes. +It incorporates multithread-safe KoBo collect changes (from Clayton), abandons J2ME support, +exposes bind and prompt attributes, and numerous contributed fixes from SurveyCTO and others. -mvn install:install-file -Dfile=javarosa-libraries-2013-09-30.jar -DgroupId=org.javarosa -DartifactId=javarosa-libraries -Dversion=2013-09-30 -Dpackaging=jar +mvn install:install-file -Dfile=javarosa-libraries-2014-03-10.jar -DgroupId=org.javarosa -DartifactId=javarosa-libraries -Dversion=2014-03-10 -Dpackaging=jar odk-httpclient-gae: diff --git a/src/main/resources/common/security.properties b/src/main/resources/common/security.properties index 78f60e7ce1..c5d024c540 100644 --- a/src/main/resources/common/security.properties +++ b/src/main/resources/common/security.properties @@ -26,7 +26,7 @@ security.server.channelType=REQUIRES_INSECURE_CHANNEL # # This is configured during install. If blank, discovers an IP address #security.server.hostname= -security.server.hostname=127.0.0.1 +security.server.hostname=sprite-1.dyn.cs.washington.edu # # any port pairings can be used. #security.server.port=80 From 718322bd35eceea1525d3481a1c231e8e8d624c2 Mon Sep 17 00:00:00 2001 From: mitchellsundt Date: Thu, 13 Mar 2014 09:48:47 -0700 Subject: [PATCH 02/13] Caution - broken Javarosa jar -- prelim update to candidate javarosa library --- .../war/WEB-INF/lib/javarosa-libraries-2013-09-30.jar | 3 --- .../war/WEB-INF/lib/javarosa-libraries-2014-03-10.jar | 3 +++ .../org/opendatakit/aggregate/odktables/api/FileService.java | 3 --- .../opendatakit/aggregate/parser/FormParserForJavaRosa.java | 4 ++-- src/main/libs/javarosa-libraries-2013-09-30.jar | 3 --- src/main/libs/javarosa-libraries-2014-03-10.jar | 3 +++ 6 files changed, 8 insertions(+), 11 deletions(-) delete mode 100644 eclipse-aggregate-gae/war/WEB-INF/lib/javarosa-libraries-2013-09-30.jar create mode 100644 eclipse-aggregate-gae/war/WEB-INF/lib/javarosa-libraries-2014-03-10.jar delete mode 100644 src/main/libs/javarosa-libraries-2013-09-30.jar create mode 100644 src/main/libs/javarosa-libraries-2014-03-10.jar diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/javarosa-libraries-2013-09-30.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/javarosa-libraries-2013-09-30.jar deleted file mode 100644 index 871c24e950..0000000000 --- a/eclipse-aggregate-gae/war/WEB-INF/lib/javarosa-libraries-2013-09-30.jar +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:bac44f6a684f6ba85a9bb781a1851adab83a6fc8fdb03ada10e4812c2869a1dd -size 356993 diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/javarosa-libraries-2014-03-10.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/javarosa-libraries-2014-03-10.jar new file mode 100644 index 0000000000..cc83b574e6 --- /dev/null +++ b/eclipse-aggregate-gae/war/WEB-INF/lib/javarosa-libraries-2014-03-10.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a6e13587352159518155604fc85055f86cb5e2585b6a6ede2a88ff930f3e9e06 +size 436908 diff --git a/src/main/java/org/opendatakit/aggregate/odktables/api/FileService.java b/src/main/java/org/opendatakit/aggregate/odktables/api/FileService.java index c3c011244b..815f354587 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/api/FileService.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/api/FileService.java @@ -18,7 +18,6 @@ import java.io.IOException; import java.util.List; -import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.Consumes; import javax.ws.rs.GET; @@ -32,8 +31,6 @@ import javax.ws.rs.core.Response; import org.jboss.resteasy.annotations.GZIP; -import org.opendatakit.aggregate.odktables.rest.ApiConstants; -import org.opendatakit.aggregate.odktables.rest.entity.TableDefinition; import org.opendatakit.common.persistence.exception.ODKTaskLockException; /** diff --git a/src/main/java/org/opendatakit/aggregate/parser/FormParserForJavaRosa.java b/src/main/java/org/opendatakit/aggregate/parser/FormParserForJavaRosa.java index eb5438130a..acb96c52fd 100644 --- a/src/main/java/org/opendatakit/aggregate/parser/FormParserForJavaRosa.java +++ b/src/main/java/org/opendatakit/aggregate/parser/FormParserForJavaRosa.java @@ -1199,7 +1199,7 @@ private void constructDataModel(final NamingSet opaque, final EntityKey k, treeElement.getName()); String persistAsColumn = originalPersistAsColumn; - switch (treeElement.dataType) { + switch (treeElement.getDataType()) { case org.javarosa.core.model.Constants.DATATYPE_TEXT: /** * Text question type. @@ -1295,7 +1295,7 @@ private void constructDataModel(final NamingSet opaque, final EntityKey k, * type otherwise * unknown */ - if (treeElement.repeatable) { + if (treeElement.isRepeatable()) { // repeatable group... opaque.removeColumnName(persistAsTable, persistAsColumn); persistAsColumn = null; diff --git a/src/main/libs/javarosa-libraries-2013-09-30.jar b/src/main/libs/javarosa-libraries-2013-09-30.jar deleted file mode 100644 index 871c24e950..0000000000 --- a/src/main/libs/javarosa-libraries-2013-09-30.jar +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:bac44f6a684f6ba85a9bb781a1851adab83a6fc8fdb03ada10e4812c2869a1dd -size 356993 diff --git a/src/main/libs/javarosa-libraries-2014-03-10.jar b/src/main/libs/javarosa-libraries-2014-03-10.jar new file mode 100644 index 0000000000..cc83b574e6 --- /dev/null +++ b/src/main/libs/javarosa-libraries-2014-03-10.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a6e13587352159518155604fc85055f86cb5e2585b6a6ede2a88ff930f3e9e06 +size 436908 From f30704394145f6948d4b9212319b15c25cbdecbf Mon Sep 17 00:00:00 2001 From: mitchellsundt Date: Sun, 16 Mar 2014 19:20:53 -0700 Subject: [PATCH 03/13] Updated javarosa jar --- .../war/WEB-INF/lib/javarosa-libraries-2014-03-10.jar | 4 ++-- .../aggregate/parser/BaseFormParserForJavaRosa.java | 2 +- src/main/libs/javarosa-libraries-2014-03-10.jar | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/javarosa-libraries-2014-03-10.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/javarosa-libraries-2014-03-10.jar index cc83b574e6..af66b59246 100644 --- a/eclipse-aggregate-gae/war/WEB-INF/lib/javarosa-libraries-2014-03-10.jar +++ b/eclipse-aggregate-gae/war/WEB-INF/lib/javarosa-libraries-2014-03-10.jar @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a6e13587352159518155604fc85055f86cb5e2585b6a6ede2a88ff930f3e9e06 -size 436908 +oid sha256:a7b9d7ebe2b58f3ac2cf803c29d009dcc3ef5a98d341c684be7cb9226297c5b9 +size 437988 diff --git a/src/main/java/org/opendatakit/aggregate/parser/BaseFormParserForJavaRosa.java b/src/main/java/org/opendatakit/aggregate/parser/BaseFormParserForJavaRosa.java index 232a7c70ad..e1b8526962 100644 --- a/src/main/java/org/opendatakit/aggregate/parser/BaseFormParserForJavaRosa.java +++ b/src/main/java/org/opendatakit/aggregate/parser/BaseFormParserForJavaRosa.java @@ -79,9 +79,9 @@ public class BaseFormParserForJavaRosa { "org.javarosa.core.model.data.DateData", // CoreModelModule "org.javarosa.core.model.data.DateTimeData", // CoreModelModule "org.javarosa.core.model.data.DecimalData", // CoreModelModule - "org.javarosa.core.model.data.GeoLineData", // CoreModelModule "org.javarosa.core.model.data.GeoPointData", // CoreModelModule "org.javarosa.core.model.data.GeoShapeData", // CoreModelModule + "org.javarosa.core.model.data.GeoTraceData", // CoreModelModule "org.javarosa.core.model.data.IntegerData", // CoreModelModule "org.javarosa.core.model.data.LongData", // CoreModelModule "org.javarosa.core.model.data.MultiPointerAnswerData", // CoreModelModule diff --git a/src/main/libs/javarosa-libraries-2014-03-10.jar b/src/main/libs/javarosa-libraries-2014-03-10.jar index cc83b574e6..af66b59246 100644 --- a/src/main/libs/javarosa-libraries-2014-03-10.jar +++ b/src/main/libs/javarosa-libraries-2014-03-10.jar @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a6e13587352159518155604fc85055f86cb5e2585b6a6ede2a88ff930f3e9e06 -size 436908 +oid sha256:a7b9d7ebe2b58f3ac2cf803c29d009dcc3ef5a98d341c684be7cb9226297c5b9 +size 437988 From 138a78f99cad54a2b3fb58194369a981f1360435 Mon Sep 17 00:00:00 2001 From: mitchellsundt Date: Tue, 18 Mar 2014 17:12:25 -0700 Subject: [PATCH 04/13] Corrects problems with Tables sync protocol and GAE. Update javarosa jar, resteasy jar and others. Update installer copy of GAE SDK. --- CONFIGURE.txt | 8 +- aggregate-gae/pom.xml | 50 +----- bitrock-installer/buildWar.xml | 2 +- .../files/appengine-java-sdk/README | 16 +- .../files/appengine-java-sdk/RELEASE_NOTES | 162 ++++++++++++++++++ .../lib/agent/appengine-agent.jar | 2 +- .../lib/agent/appengine-agentimpl.jar | 4 +- .../lib/impl/agent/appengine-agentruntime.jar | 2 +- .../lib/impl/appengine-api-labs.jar | 4 +- .../lib/impl/appengine-api-stubs.jar | 4 +- .../lib/impl/appengine-api.jar | 4 +- .../lib/impl/appengine-local-runtime.jar | 4 +- .../lib/impl/google_sql.jar | 4 +- .../v1/appengine-local-endpoints.jar | 4 +- .../v1/appengine-api-labs.jar | 4 +- .../v1/appengine-endpoints.jar | 4 +- .../jsr107/v1/appengine-jsr107cache-1.8.5.jar | 3 - .../jsr107/v1/appengine-jsr107cache-1.9.0.jar | 3 + .../override/appengine-dev-jdk-overrides.jar | 2 +- .../shared/appengine-local-runtime-shared.jar | 4 +- .../appengine-java-sdk/lib/shared/el-api.jar | 4 +- .../appengine-java-sdk/lib/shared/jsp-api.jar | 4 +- .../lib/shared/servlet-api.jar | 4 +- .../lib/testing/appengine-testing.jar | 4 +- .../lib/user/appengine-api-1.0-sdk-1.8.5.jar | 3 - .../lib/user/appengine-api-1.0-sdk-1.9.0.jar | 3 + .../lib/user/appengine-api-labs-1.8.5.jar | 3 - .../lib/user/appengine-api-labs-1.9.0.jar | 3 + .../lib/user/appengine-jsr107cache-1.8.5.jar | 3 - .../lib/user/appengine-jsr107cache-1.9.0.jar | 3 + .../lib/user/libservice.jar | 3 + eclipse-aggregate-gae/.classpath | 33 ++-- .../lib/google-api-client-1.16.0-rc.jar | 3 - .../lib/google-api-client-1.17.0-rc.jar | 3 + ...api-services-drive-v2-rev116-1.17.0-rc.jar | 3 + ...-api-services-drive-v2-rev93-1.16.0-rc.jar | 3 - .../lib/google-http-client-1.16.0-rc.jar | 3 - .../lib/google-http-client-1.17.0-rc.jar | 3 + ...google-http-client-appengine-1.16.0-rc.jar | 3 - ...google-http-client-appengine-1.17.0-rc.jar | 3 + .../google-http-client-jackson-1.16.0-rc.jar | 3 - .../google-http-client-jackson-1.17.0-rc.jar | 3 + .../lib/google-http-client-jdo-1.17.0-rc.jar | 3 + .../lib/google-oauth-client-1.16.0-rc.jar | 3 - .../lib/google-oauth-client-1.17.0-rc.jar | 3 + ...oogle-oauth-client-appengine-1.16.0-rc.jar | 3 - ...oogle-oauth-client-appengine-1.17.0-rc.jar | 3 + .../google-oauth-client-servlet-1.17.0-rc.jar | 3 + .../WEB-INF/lib/jackson-core-asl-1.9.11.jar | 3 - .../WEB-INF/lib/jackson-core-asl-1.9.13.jar | 3 + .../WEB-INF/lib/jackson-mapper-asl-1.9.11.jar | 3 - .../WEB-INF/lib/jackson-mapper-asl-1.9.13.jar | 3 + .../war/WEB-INF/lib/jaxrs-api-2.3.3.Final.jar | 3 - .../war/WEB-INF/lib/jaxrs-api-3.0.6.Final.jar | 3 + ...s-annotations-api_1.1_spec-1.0.1.Final.jar | 3 + .../war/WEB-INF/lib/jdom-1.1.2.jar | 3 - .../war/WEB-INF/lib/jsr250-api-1.0.jar | 3 - .../war/WEB-INF/lib/stax-api-1.0-2.jar | 3 - .../war/WEB-INF/lib/stax-api-1.0.1.jar | 3 + .../war/WEB-INF/lib/transaction-api-1.1.jar | 3 + eclipse-aggregate-mysql/.classpath | 27 +-- pom.xml | 69 +++++--- .../aggregate/constants/common/UIConsts.java | 2 +- ...ntentEncodingSuppressionRequestFilter.java | 71 ++++++++ ...tentEncodingSuppressionResponseFilter.java | 70 ++++++++ .../impl/api/FileManifestServiceImpl.java | 5 +- .../odktables/impl/api/FileServiceImpl.java | 5 +- .../odktables/impl/api/IOExceptionMapper.java | 1 + .../impl/api/ODKDatastoreExceptionMapper.java | 1 + .../impl/api/ODKTablesAPIApplication.java | 2 + .../impl/api/ODKTablesExceptionMapper.java | 1 + .../impl/api/ODKTaskLockExceptionMapper.java | 1 + .../impl/api/RuntimeExceptionMapper.java | 1 + .../odktables/impl/api/ServiceUtils.java | 24 +++ .../odktables/impl/api/TableServiceImpl.java | 5 +- .../parser/BaseFormParserForJavaRosa.java | 16 +- .../opendatakit/common/security/Realm.java | 28 ++- src/main/resources/common/security.properties | 2 +- src/main/resources/gae-unit/odk-settings.xml | 1 + src/main/resources/gae/odk-settings.xml | 1 + .../resources/mysql-unit/odk-settings.xml | 1 + src/main/resources/mysql/odk-settings.xml | 1 + .../resources/postgres-unit/odk-settings.xml | 1 + src/main/resources/postgres/odk-settings.xml | 1 + .../aggregate/odktables/DataManagerTest.java | 1 - .../aggregate/odktables/TableManagerTest.java | 6 +- 86 files changed, 573 insertions(+), 215 deletions(-) delete mode 100644 bitrock-installer/files/appengine-java-sdk/lib/opt/user/jsr107/v1/appengine-jsr107cache-1.8.5.jar create mode 100644 bitrock-installer/files/appengine-java-sdk/lib/opt/user/jsr107/v1/appengine-jsr107cache-1.9.0.jar delete mode 100644 bitrock-installer/files/appengine-java-sdk/lib/user/appengine-api-1.0-sdk-1.8.5.jar create mode 100644 bitrock-installer/files/appengine-java-sdk/lib/user/appengine-api-1.0-sdk-1.9.0.jar delete mode 100644 bitrock-installer/files/appengine-java-sdk/lib/user/appengine-api-labs-1.8.5.jar create mode 100644 bitrock-installer/files/appengine-java-sdk/lib/user/appengine-api-labs-1.9.0.jar delete mode 100644 bitrock-installer/files/appengine-java-sdk/lib/user/appengine-jsr107cache-1.8.5.jar create mode 100644 bitrock-installer/files/appengine-java-sdk/lib/user/appengine-jsr107cache-1.9.0.jar create mode 100644 bitrock-installer/files/appengine-java-sdk/lib/user/libservice.jar delete mode 100644 eclipse-aggregate-gae/war/WEB-INF/lib/google-api-client-1.16.0-rc.jar create mode 100644 eclipse-aggregate-gae/war/WEB-INF/lib/google-api-client-1.17.0-rc.jar create mode 100644 eclipse-aggregate-gae/war/WEB-INF/lib/google-api-services-drive-v2-rev116-1.17.0-rc.jar delete mode 100644 eclipse-aggregate-gae/war/WEB-INF/lib/google-api-services-drive-v2-rev93-1.16.0-rc.jar delete mode 100644 eclipse-aggregate-gae/war/WEB-INF/lib/google-http-client-1.16.0-rc.jar create mode 100644 eclipse-aggregate-gae/war/WEB-INF/lib/google-http-client-1.17.0-rc.jar delete mode 100644 eclipse-aggregate-gae/war/WEB-INF/lib/google-http-client-appengine-1.16.0-rc.jar create mode 100644 eclipse-aggregate-gae/war/WEB-INF/lib/google-http-client-appengine-1.17.0-rc.jar delete mode 100644 eclipse-aggregate-gae/war/WEB-INF/lib/google-http-client-jackson-1.16.0-rc.jar create mode 100644 eclipse-aggregate-gae/war/WEB-INF/lib/google-http-client-jackson-1.17.0-rc.jar create mode 100644 eclipse-aggregate-gae/war/WEB-INF/lib/google-http-client-jdo-1.17.0-rc.jar delete mode 100644 eclipse-aggregate-gae/war/WEB-INF/lib/google-oauth-client-1.16.0-rc.jar create mode 100644 eclipse-aggregate-gae/war/WEB-INF/lib/google-oauth-client-1.17.0-rc.jar delete mode 100644 eclipse-aggregate-gae/war/WEB-INF/lib/google-oauth-client-appengine-1.16.0-rc.jar create mode 100644 eclipse-aggregate-gae/war/WEB-INF/lib/google-oauth-client-appengine-1.17.0-rc.jar create mode 100644 eclipse-aggregate-gae/war/WEB-INF/lib/google-oauth-client-servlet-1.17.0-rc.jar delete mode 100644 eclipse-aggregate-gae/war/WEB-INF/lib/jackson-core-asl-1.9.11.jar create mode 100644 eclipse-aggregate-gae/war/WEB-INF/lib/jackson-core-asl-1.9.13.jar delete mode 100644 eclipse-aggregate-gae/war/WEB-INF/lib/jackson-mapper-asl-1.9.11.jar create mode 100644 eclipse-aggregate-gae/war/WEB-INF/lib/jackson-mapper-asl-1.9.13.jar delete mode 100644 eclipse-aggregate-gae/war/WEB-INF/lib/jaxrs-api-2.3.3.Final.jar create mode 100644 eclipse-aggregate-gae/war/WEB-INF/lib/jaxrs-api-3.0.6.Final.jar create mode 100644 eclipse-aggregate-gae/war/WEB-INF/lib/jboss-annotations-api_1.1_spec-1.0.1.Final.jar delete mode 100644 eclipse-aggregate-gae/war/WEB-INF/lib/jdom-1.1.2.jar delete mode 100644 eclipse-aggregate-gae/war/WEB-INF/lib/jsr250-api-1.0.jar delete mode 100644 eclipse-aggregate-gae/war/WEB-INF/lib/stax-api-1.0-2.jar create mode 100644 eclipse-aggregate-gae/war/WEB-INF/lib/stax-api-1.0.1.jar create mode 100644 eclipse-aggregate-gae/war/WEB-INF/lib/transaction-api-1.1.jar create mode 100644 src/main/java/org/opendatakit/aggregate/odktables/impl/api/AppEngineContentEncodingSuppressionRequestFilter.java create mode 100644 src/main/java/org/opendatakit/aggregate/odktables/impl/api/AppEngineContentEncodingSuppressionResponseFilter.java diff --git a/CONFIGURE.txt b/CONFIGURE.txt index f3f834c8dc..358ad19e5d 100644 --- a/CONFIGURE.txt +++ b/CONFIGURE.txt @@ -70,8 +70,8 @@ Windows / Preferences: Google / App Engine: - Be sure there is an App Engine 1.8.0 or higher SDK defined. - If the default App Engine version is not 1.8.0, you may need to + Be sure there is an App Engine 1.9.0 or higher SDK defined. + If the default App Engine version is not 1.9.0, you may need to tweak with the project settings to get the newer jars to be added. Google / Web Toolkit: @@ -161,7 +161,7 @@ Choose 'Web Application' Open this up, and select 'index.html' -On the '(X)= Arguments' tab, insert the following at the front of the 'VM Arguments' section: +On the '(X)= Arguments' tab, replace the '-Xmx512m' at the front of the 'VM Arguments' section with: -Xmx2048m -XX:MaxPermSize=1536m @@ -371,7 +371,7 @@ A minimal file is: ${user.home}/.m2/repository C:\\Users\\Administrator\\AppData\\Local\\Temp - \C:\\Users\\Administrator\\appengine-java-sdk-1.8.0 + \C:\\Users\\Administrator\\appengine-java-sdk-1.9.0 C:\Program Files (x86)\BitRock InstallBuilder Professional 8.6.0 \C:\\Users\\Administrator\\keystore\\jarSignerDetails.txt no diff --git a/aggregate-gae/pom.xml b/aggregate-gae/pom.xml index b8255ba10f..04832603af 100644 --- a/aggregate-gae/pom.xml +++ b/aggregate-gae/pom.xml @@ -136,56 +136,22 @@ net.kindleit gae-runtime - ${gooogle.appengine.sdk.version} + 1.8.0 pom + + + * + * + + com.google.appengine appengine-tools-sdk ${gooogle.appengine.sdk.version} - - - org.apache.geronimo.specs - geronimo-jta_1.1_spec - 1.1.1 - runtime - - - - org.apache.geronimo.specs - geronimo-jpa_3.0_spec - 1.1.1 - runtime - - - - javax.jdo - jdo2-api - 2.3-eb - runtime - - - - org.datanucleus - datanucleus-core - 1.1.5 - - - - com.google.appengine.orm - datanucleus-appengine - 1.0.10 - runtime - - - - org.datanucleus - datanucleus-jpa - 1.1.5 - runtime - + diff --git a/bitrock-installer/buildWar.xml b/bitrock-installer/buildWar.xml index 49b982c5f7..494ed3f242 100644 --- a/bitrock-installer/buildWar.xml +++ b/bitrock-installer/buildWar.xml @@ -1,7 +1,7 @@ ODKAggregate ODK Aggregate - 1.4.2 Production + 1.4.3 Production files/LICENSE.txt files/leftSide.png files/logo.png diff --git a/bitrock-installer/files/appengine-java-sdk/README b/bitrock-installer/files/appengine-java-sdk/README index 103d8a8458..90a6bcb2c7 100644 --- a/bitrock-installer/files/appengine-java-sdk/README +++ b/bitrock-installer/files/appengine-java-sdk/README @@ -25,27 +25,27 @@ This bundle contains the following: bin/appcfg - Deploys Java apps to Google bin/dev_appserver - Launches the development server - lib/user/ - API's we provide to you to build your + lib/user/ - API's we provide to you to build your application. They should be bundled with your webapp (in WEB-INF/lib). lib/shared/ - API's intrinsic to App Engine such as the - Servlet and JSP libraries. You must not + Servlet and JSP libraries. You must not bundle these with your webapp. - + config/ - Configuration files config/sdk - Configuration files for the SDK tools config/user - Configuration files for your applications - + demos/ - Sample applications Dependencies ------------ -- Java 1.5 +- Java 1.7 Writing an Application ---------------------- - + Google App Engine for Java web applications are based on the Java Servlet API. This means that you will want to provide classes that extend either the javax.servlet.GenericServlet or @@ -71,11 +71,11 @@ Running the Local Runtime Environment All executable scripts live in the bin directory of the SDK. Windows scripts have a ".cmd" extension, Unix and MacOSX scripts end with ".sh". The commands -below assume you're running them from the root directory of the SDK. +below assume you're running them from the root directory of the SDK. To run the local development server use: bin\dev_appserver.cmd - + It will begin listening on port 8080 on the local machine. Many of the sample applications have Ant build.xml targets that start diff --git a/bitrock-installer/files/appengine-java-sdk/RELEASE_NOTES b/bitrock-installer/files/appengine-java-sdk/RELEASE_NOTES index b044d90411..f287995d8d 100644 --- a/bitrock-installer/files/appengine-java-sdk/RELEASE_NOTES +++ b/bitrock-installer/files/appengine-java-sdk/RELEASE_NOTES @@ -3,6 +3,168 @@ All rights reserved. App Engine Java SDK - Release Notes +Version 1.9.0 +============= +- Java 6 applications cannot be deployed to Google App Engine from any version + of the SDK. Existing Java 6 applications will continue to run. If you are + still relying on a Java 6 application in Google App Engine, we strongly + encourage you to start testing and deploying your app using Java 7. Java 7 + has been certified by Oracle to be fully backwards compatible with Java 6, + as well as providing a number of benefits to developers. Java 7 applications + will be fully supported in future releases of the App Engine SDK. + If you absolutely need to continue to deploy Java 6 applications for + compatibility reasons, you can request that your application be whitelisted + for Java 6 deployment from http://goo.gl/ycffXq. As we review each whitelist + application by hand, please allow 10 working days for review, and note that + only billed applications that show a proven incompatibility will be + considered for exemption. +- Newly created App Engine applications now have a Google Cloud Storage bucket + created automatically as part of the same Google Cloud Project. For more + information please see: + https://developers.google.com/appengine/docs/java/googlecloudstorageclient/ + activate#Using_the_default_Gcs_bucket +- Modules Service API and application management features are now GA. +- Removed deprecated ModulesService methods and exceptions: + getModulesAsync() + getVersionsAsync() + getDefaultVersionAsync() + getNumInstancesAsync() + getVersionHostnameAsync() + getInstanceHostnameAsync() + startModule() (replaced by startVersion) + startModuleAsync() (replaced by startVersionAsync) + stopModule() (replaced by stopVersion) + stopModuleAsync() (replaced by stopVersionAsync) + getModuleHostname() (replaced by getVersionHostname and getInstanceHostname) + getModuleHostnameAsync() + InvalidInstanceException + InvalidModuloeException + InvalidVersionException +- Moved ModulesService from package com.google.appengine.api.labs.modules to + package com.google.appengine.api.modules and from appengine-api-labs.jar to + appengine-api.jar. +- The following deprecated methods in com.google.appengine.api.log.LogQuery + will be removed in an upcoming release. + Builder.withModuleVersions(List> moduleVersionIds) + Builder.withServerVersions(List> serverVersionIds) + moduleVersions(List> moduleVersions) + serverVersions(List> serverVersions) + getModuleVersions() + getServerVersions() + To avoid breakage when these are removed please update your code as per the + javadocs and upload the updated version. +- MapReduce is now a Preview feature. Documentation and getting started guides + are now available on + https://developers.google.com/appengine/docs/java/dataprocessing/. +- The size limit on the Search API is now computed and enforced on a per-index + basis, rather than for the app as a whole. The per-index limit is now 10GB. + There is no fixed limit on the number of indexes, or on the total amount of + Search API storage an application may use. +- Users now have the ability to embed images in emails via the Content-Id + attachment header. + https://code.google.com/p/googleappengine/issues/detail?id=965 + https://code.google.com/p/googleappengine/issues/detail?id=10503 +- New App Engine Application Identifiers must now start with a letter, + in addition to the existing requirements that the identifier be 6-30 + characters which are letters, numbers, and hyphens, and not start or end with + a hyphen. + +Version 1.8.9 +============= +- Renamed appcfg start to appcfg start_module_version and stop to + stop_module_version. +- The following methods are deprecated in the Modules API and will be removed + in an upcoming release: + getModulesAsync + getVersionsAsync + getDefaultVersionAsync + getNumInstancesAsync + getVersionHostnameAsync + getInstanceHostnameAsync + startModule (replaced by startVersion) + startModuleAsync (replaced by startVersionAsync) + stopModule (replaced by stopVersion) + stopModuleAsync (replaced by stopVersionAsync) + getModuleHostname (replaced by getVersionHostname and getInstanceHostname) + getModuleHostnameAsync +- The following exceptions have been deprecated in the Modules API and will be + removed in a following release: + InvalidInstanceException + InvalidModuloeException + InvalidVersionException +- The following exceptions have been removed in the Modules API: + ModuleAlreadyStartedException + ModuleAlreadyStoppedException +- Modules are now supported in the dev_appserver Development Console. +- Fixed an issue with ID allocation collisions in the Datastore. + https://code.google.com/p/googleappengine/issues/detail?id=10134 + +Version 1.8.8 +============= +- Dedicated Memcache is now a GA feature. Our deprecation policy applies and + customers are encouraged to use this feature in production. +- Memcache API calls are tracked by a new metric that estimates resource usage, + Memcache Compute Units (MCU's). There is a new quota bucket and + dashboard graph corresponding to the metric, as well as an option in the + memcache viewer to display top keys by MCU's. +- Fixed an issue with cloning apps not creating proper service account names. + https://code.google.com/p/googleappengine/issues/detail?id=10235 +- Fixed an issue with users receiving errors when uploading large files to GCS. + https://code.google.com/p/googleappengine/issues/detail?id=10101 + +Version 1.8.7 +============= +- Cloud Endpoints is now a GA feature. +- Memcache configuration changes and mutations (flush, set, etc.) made from the + Admin Console are now recorded in the admin console logs. +- The max_concurrent_requests setting is now configurable per version/module. + http://code.google.com/p/googleappengine/issues/detail?id=7927 +- LogService API functions related to the modules feature will be changing. + The following functions will be deprecated and new versions will be provided: + LogQuery.getModuleVersions(), LogQuery.moduleVersions() and + LogQuery.Builder.withModuleVersions(). + The following functions will be removed in a future release: + LogQuery.getModuleVersions(), LogQuery.moduleVersions(), + LogQuery.Builder.withModuleVersions(), LogQuery.getServerVersions(), + LogQuery.serverVersions() and LogQuery.Builder.withServerVersions(). + Serialized LogQuery objects with values set using any of the above removed + methods will not be supported in a future release. +- Fixed an issue with Datastore not correctly validating namespaces with + greater than 500 characters. +- Fixed and issue with vacuum_indexes not working in the SDK. + https://code.google.com/p/googleappengine/issues/detail?id=10168 + +Version 1.8.6 +============= +- GetAccessToken in the OAuth package is now a GA feature. +- The LogsService API functions related to the Modules feature will be + changing as follows: + The following functions will be deprecated and new versions will be + provided: LogQuery.getModuleVersions(), LogQuery.moduleVersions() and + LogQuery.Builder.withModuleVersions(). + The following functions will be removed in an upcoming release: + LogQuery.getModuleVersions(), LogQuery.moduleVersions() and + LogQuery.Builder.withModuleVersions(), LogQuery.getServerVersions(), + LogQuery.serverVersions() and LogQuery.Builder.withServerVersions(). + Serialized LogQuery objects with values set using any of the above removed + methods will not be supported in an upcoming release. +- A memcache size chart has been added to admin console's dashboard. Access it + via the drop-down above the graph. The chart graphs memcache size over time + enabling customers to determine when cache flush events occurred. This is a + preview feature. +- The Cloud Endpoints API @ApiSerializer has been renamed to @ApiTransformer + and the Serializer interface has been renamed to Transformer. + @ApiSerializationProperty has also been renamed to @ApiResourceProperty. +- If a customer passes invalid data to a memcache put() call, they now see a + more useful error message. + https://code.google.com/p/googleappengine/issues/detail?id=8671 +- Fixed an issue with the SDK that allows an invalid Datastore query combination + of group by and filter properties. +- Fixed an issue affecting validation of the size of Datastore property names. +- Fixed an issue with Datastore query validation for strings with exactly 500 + characters. + https://code.google.com/p/googleappengine/issues/detail?id=10019 + Version 1.8.5 ============= - The Search API is now a GA feature. diff --git a/bitrock-installer/files/appengine-java-sdk/lib/agent/appengine-agent.jar b/bitrock-installer/files/appengine-java-sdk/lib/agent/appengine-agent.jar index 966a6126e8..d150f50ca8 100644 --- a/bitrock-installer/files/appengine-java-sdk/lib/agent/appengine-agent.jar +++ b/bitrock-installer/files/appengine-java-sdk/lib/agent/appengine-agent.jar @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9eccefc7f42ee813bf4c8395ae597beff4f86ffd3c57e674aa6b123a9f7ff748 +oid sha256:d14bc4ff6e49abb34341c088de6a0aa6e67c8bab10bf9b9ffb98eca4fd558fb4 size 16959 diff --git a/bitrock-installer/files/appengine-java-sdk/lib/agent/appengine-agentimpl.jar b/bitrock-installer/files/appengine-java-sdk/lib/agent/appengine-agentimpl.jar index fc9878f31d..234c8131c6 100644 --- a/bitrock-installer/files/appengine-java-sdk/lib/agent/appengine-agentimpl.jar +++ b/bitrock-installer/files/appengine-java-sdk/lib/agent/appengine-agentimpl.jar @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6956c74d47f59e9e9cf63091003ecac82368d6c7f56d47ff0f611f5b05ad7206 -size 144614 +oid sha256:e2c42bb92c0a3b0bc1a92c71d7c648db7bcc6524eb7e2f51759bd464648bf84b +size 331041 diff --git a/bitrock-installer/files/appengine-java-sdk/lib/impl/agent/appengine-agentruntime.jar b/bitrock-installer/files/appengine-java-sdk/lib/impl/agent/appengine-agentruntime.jar index 27568f91a8..b98426a3de 100644 --- a/bitrock-installer/files/appengine-java-sdk/lib/impl/agent/appengine-agentruntime.jar +++ b/bitrock-installer/files/appengine-java-sdk/lib/impl/agent/appengine-agentruntime.jar @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8377a7a1d4672cfe2d078b51b400f218f107d2b8d62b3300e8f0a199f972081a +oid sha256:191a7ac684a1ceeb21109cfaf0802cd9b1d00b9c790c707a86c910373250075c size 42860 diff --git a/bitrock-installer/files/appengine-java-sdk/lib/impl/appengine-api-labs.jar b/bitrock-installer/files/appengine-java-sdk/lib/impl/appengine-api-labs.jar index 2f46b57d65..969bd33e79 100644 --- a/bitrock-installer/files/appengine-java-sdk/lib/impl/appengine-api-labs.jar +++ b/bitrock-installer/files/appengine-java-sdk/lib/impl/appengine-api-labs.jar @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:df97e1e6165aa226a11b66bc7afc947ad5487b76e0df46294b89605b1b9284ac -size 11822636 +oid sha256:82a5f0df934dceaa59aa6bc07b3601ea5a85568ef4eae37bcc038e42c3ddd91a +size 13627943 diff --git a/bitrock-installer/files/appengine-java-sdk/lib/impl/appengine-api-stubs.jar b/bitrock-installer/files/appengine-java-sdk/lib/impl/appengine-api-stubs.jar index 0baa2b20a6..098ccb4d69 100644 --- a/bitrock-installer/files/appengine-java-sdk/lib/impl/appengine-api-stubs.jar +++ b/bitrock-installer/files/appengine-java-sdk/lib/impl/appengine-api-stubs.jar @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f5035786ad1e17a0c6c7784335615a4bf7aefdab0fc4737d5c10fabf01b7bb7b -size 6144889 +oid sha256:33e1c8eca2e9b05e750c1a7008a9ac2b0dba3ec67c879b484e1d4e437525eab2 +size 5819805 diff --git a/bitrock-installer/files/appengine-java-sdk/lib/impl/appengine-api.jar b/bitrock-installer/files/appengine-java-sdk/lib/impl/appengine-api.jar index 96586715db..5faa585588 100644 --- a/bitrock-installer/files/appengine-java-sdk/lib/impl/appengine-api.jar +++ b/bitrock-installer/files/appengine-java-sdk/lib/impl/appengine-api.jar @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7449ee33c5835edd0f3de72338e6a90027e3a5beae748b1f3f060f28ab04cb78 -size 12588505 +oid sha256:2d1a023cc7133b4c9401f64ce8b4be85fef45ce94279db8e1b8660a9ff944e7d +size 12808203 diff --git a/bitrock-installer/files/appengine-java-sdk/lib/impl/appengine-local-runtime.jar b/bitrock-installer/files/appengine-java-sdk/lib/impl/appengine-local-runtime.jar index 1908cc3882..94a467bcdc 100644 --- a/bitrock-installer/files/appengine-java-sdk/lib/impl/appengine-local-runtime.jar +++ b/bitrock-installer/files/appengine-java-sdk/lib/impl/appengine-local-runtime.jar @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:556c15071347297f0ae7145ca04f1663f16c12bf7dac7931ee2369baa0b3e46e -size 7093844 +oid sha256:e9f3203f7c8b7adf49605137fee2865d169b80e2a13e84953614bc64c0c7a770 +size 7084474 diff --git a/bitrock-installer/files/appengine-java-sdk/lib/impl/google_sql.jar b/bitrock-installer/files/appengine-java-sdk/lib/impl/google_sql.jar index 10691a0030..e631b88ada 100644 --- a/bitrock-installer/files/appengine-java-sdk/lib/impl/google_sql.jar +++ b/bitrock-installer/files/appengine-java-sdk/lib/impl/google_sql.jar @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fa6d7b5a704649d12dc3e4d5eae0fe66e8d452abf319bd7446dd89c00b816f92 -size 5155134 +oid sha256:9fd6b64b4a174fc5784ed0297c5f04978fc795833b2c322476b4e85a33b45594 +size 5186883 diff --git a/bitrock-installer/files/appengine-java-sdk/lib/opt/tools/appengine-local-endpoints/v1/appengine-local-endpoints.jar b/bitrock-installer/files/appengine-java-sdk/lib/opt/tools/appengine-local-endpoints/v1/appengine-local-endpoints.jar index d7e3012a2e..1fcc14e43a 100644 --- a/bitrock-installer/files/appengine-java-sdk/lib/opt/tools/appengine-local-endpoints/v1/appengine-local-endpoints.jar +++ b/bitrock-installer/files/appengine-java-sdk/lib/opt/tools/appengine-local-endpoints/v1/appengine-local-endpoints.jar @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c8da18fcdec32ebc9813fa55abdd8ba3cbbe66b2ecf95894b8cfc953b9678baa -size 80186 +oid sha256:3bbbb861589c799b1b037bca4378c775fc722df2ccd4b85ef06d1e132e89fd81 +size 81857 diff --git a/bitrock-installer/files/appengine-java-sdk/lib/opt/user/appengine-api-labs/v1/appengine-api-labs.jar b/bitrock-installer/files/appengine-java-sdk/lib/opt/user/appengine-api-labs/v1/appengine-api-labs.jar index 2f46b57d65..969bd33e79 100644 --- a/bitrock-installer/files/appengine-java-sdk/lib/opt/user/appengine-api-labs/v1/appengine-api-labs.jar +++ b/bitrock-installer/files/appengine-java-sdk/lib/opt/user/appengine-api-labs/v1/appengine-api-labs.jar @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:df97e1e6165aa226a11b66bc7afc947ad5487b76e0df46294b89605b1b9284ac -size 11822636 +oid sha256:82a5f0df934dceaa59aa6bc07b3601ea5a85568ef4eae37bcc038e42c3ddd91a +size 13627943 diff --git a/bitrock-installer/files/appengine-java-sdk/lib/opt/user/appengine-endpoints/v1/appengine-endpoints.jar b/bitrock-installer/files/appengine-java-sdk/lib/opt/user/appengine-endpoints/v1/appengine-endpoints.jar index 9129d7012e..059070e48e 100644 --- a/bitrock-installer/files/appengine-java-sdk/lib/opt/user/appengine-endpoints/v1/appengine-endpoints.jar +++ b/bitrock-installer/files/appengine-java-sdk/lib/opt/user/appengine-endpoints/v1/appengine-endpoints.jar @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:430c1734d4f48ca676620f314c6fbe9ba9cd27e9bf3f0880daa18bf28f97332a -size 1973714 +oid sha256:c31abb037cd4c0e2764381e3f9e779ebc6bccfade4cf8f41077f3db7c52563bd +size 1970369 diff --git a/bitrock-installer/files/appengine-java-sdk/lib/opt/user/jsr107/v1/appengine-jsr107cache-1.8.5.jar b/bitrock-installer/files/appengine-java-sdk/lib/opt/user/jsr107/v1/appengine-jsr107cache-1.8.5.jar deleted file mode 100644 index fe206ba601..0000000000 --- a/bitrock-installer/files/appengine-java-sdk/lib/opt/user/jsr107/v1/appengine-jsr107cache-1.8.5.jar +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2f72a4f76d29b6864e163df3ab6a770a2a90e8a2944f2bbcee7a9c07d617c82f -size 7064 diff --git a/bitrock-installer/files/appengine-java-sdk/lib/opt/user/jsr107/v1/appengine-jsr107cache-1.9.0.jar b/bitrock-installer/files/appengine-java-sdk/lib/opt/user/jsr107/v1/appengine-jsr107cache-1.9.0.jar new file mode 100644 index 0000000000..6fcfa4cdf4 --- /dev/null +++ b/bitrock-installer/files/appengine-java-sdk/lib/opt/user/jsr107/v1/appengine-jsr107cache-1.9.0.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:be66451da694862a7f6f90de8e008b76b40d52442618be76bfd000e28f416840 +size 7131 diff --git a/bitrock-installer/files/appengine-java-sdk/lib/override/appengine-dev-jdk-overrides.jar b/bitrock-installer/files/appengine-java-sdk/lib/override/appengine-dev-jdk-overrides.jar index 29e643ef01..a0e9451e87 100644 --- a/bitrock-installer/files/appengine-java-sdk/lib/override/appengine-dev-jdk-overrides.jar +++ b/bitrock-installer/files/appengine-java-sdk/lib/override/appengine-dev-jdk-overrides.jar @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:134484adcb1216c78a4f1d860debd96d7a7291062023bbe65859b86b9c157786 +oid sha256:178977ec2463b97eacd11c0342f9a83859f44a7c1ffba53f1b4902a61ce2275b size 1007 diff --git a/bitrock-installer/files/appengine-java-sdk/lib/shared/appengine-local-runtime-shared.jar b/bitrock-installer/files/appengine-java-sdk/lib/shared/appengine-local-runtime-shared.jar index bc7fa8ff9d..ef3c0eea35 100644 --- a/bitrock-installer/files/appengine-java-sdk/lib/shared/appengine-local-runtime-shared.jar +++ b/bitrock-installer/files/appengine-java-sdk/lib/shared/appengine-local-runtime-shared.jar @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:15639ce4e86840c225caa397975670872ded440dcbb70ecb61a8576f34754508 -size 247962 +oid sha256:d8d52d4bab3ed57b229adf819315b92671a1de64afa7931165fc2fbf4b7d7f5d +size 247098 diff --git a/bitrock-installer/files/appengine-java-sdk/lib/shared/el-api.jar b/bitrock-installer/files/appengine-java-sdk/lib/shared/el-api.jar index 1d3ddd7284..970d183e1f 100644 --- a/bitrock-installer/files/appengine-java-sdk/lib/shared/el-api.jar +++ b/bitrock-installer/files/appengine-java-sdk/lib/shared/el-api.jar @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cd5bb7ecc4fddf55fc1c48bfc256c0a911d3323c61f1053af437ad576f29ce8f -size 29848 +oid sha256:46a055490bc85fb44d8aec07be95d66456aaa4d943e50b8ebea8f1e1c25a4c50 +size 29861 diff --git a/bitrock-installer/files/appengine-java-sdk/lib/shared/jsp-api.jar b/bitrock-installer/files/appengine-java-sdk/lib/shared/jsp-api.jar index 09106b50c9..fe5b6bc09f 100644 --- a/bitrock-installer/files/appengine-java-sdk/lib/shared/jsp-api.jar +++ b/bitrock-installer/files/appengine-java-sdk/lib/shared/jsp-api.jar @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f780aed430f1540489728092b347e955ba298c5fb36c1df7765a37d259dcec27 -size 77114 +oid sha256:9d56c21661bb8c540b8159f0935c550e7a20677d696302bc1f823143c1e2c7e6 +size 77127 diff --git a/bitrock-installer/files/appengine-java-sdk/lib/shared/servlet-api.jar b/bitrock-installer/files/appengine-java-sdk/lib/shared/servlet-api.jar index f0edbcb270..a47a61255b 100644 --- a/bitrock-installer/files/appengine-java-sdk/lib/shared/servlet-api.jar +++ b/bitrock-installer/files/appengine-java-sdk/lib/shared/servlet-api.jar @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d5c384e9d767b146840db35de6fef2227a27ebff0c8cd25a0c1b8db02e97e6ad -size 90388 +oid sha256:f6698e63e936a3802302c67ba48c7ae013810c9c7aba9e41f44a5b55a89a9ea7 +size 90401 diff --git a/bitrock-installer/files/appengine-java-sdk/lib/testing/appengine-testing.jar b/bitrock-installer/files/appengine-java-sdk/lib/testing/appengine-testing.jar index 86a502cd02..13851a98dc 100644 --- a/bitrock-installer/files/appengine-java-sdk/lib/testing/appengine-testing.jar +++ b/bitrock-installer/files/appengine-java-sdk/lib/testing/appengine-testing.jar @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:95c935d7b221372c86b75733e41bb7962365bd7b3cf3f4316000781632c0cd46 -size 4221977 +oid sha256:5099edf87ea8b94cdaa52b3fb11d9da69fae9423f626aa5b64d22200e25a6ea9 +size 4111554 diff --git a/bitrock-installer/files/appengine-java-sdk/lib/user/appengine-api-1.0-sdk-1.8.5.jar b/bitrock-installer/files/appengine-java-sdk/lib/user/appengine-api-1.0-sdk-1.8.5.jar deleted file mode 100644 index 96586715db..0000000000 --- a/bitrock-installer/files/appengine-java-sdk/lib/user/appengine-api-1.0-sdk-1.8.5.jar +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7449ee33c5835edd0f3de72338e6a90027e3a5beae748b1f3f060f28ab04cb78 -size 12588505 diff --git a/bitrock-installer/files/appengine-java-sdk/lib/user/appengine-api-1.0-sdk-1.9.0.jar b/bitrock-installer/files/appengine-java-sdk/lib/user/appengine-api-1.0-sdk-1.9.0.jar new file mode 100644 index 0000000000..5faa585588 --- /dev/null +++ b/bitrock-installer/files/appengine-java-sdk/lib/user/appengine-api-1.0-sdk-1.9.0.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2d1a023cc7133b4c9401f64ce8b4be85fef45ce94279db8e1b8660a9ff944e7d +size 12808203 diff --git a/bitrock-installer/files/appengine-java-sdk/lib/user/appengine-api-labs-1.8.5.jar b/bitrock-installer/files/appengine-java-sdk/lib/user/appengine-api-labs-1.8.5.jar deleted file mode 100644 index 2f46b57d65..0000000000 --- a/bitrock-installer/files/appengine-java-sdk/lib/user/appengine-api-labs-1.8.5.jar +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:df97e1e6165aa226a11b66bc7afc947ad5487b76e0df46294b89605b1b9284ac -size 11822636 diff --git a/bitrock-installer/files/appengine-java-sdk/lib/user/appengine-api-labs-1.9.0.jar b/bitrock-installer/files/appengine-java-sdk/lib/user/appengine-api-labs-1.9.0.jar new file mode 100644 index 0000000000..969bd33e79 --- /dev/null +++ b/bitrock-installer/files/appengine-java-sdk/lib/user/appengine-api-labs-1.9.0.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:82a5f0df934dceaa59aa6bc07b3601ea5a85568ef4eae37bcc038e42c3ddd91a +size 13627943 diff --git a/bitrock-installer/files/appengine-java-sdk/lib/user/appengine-jsr107cache-1.8.5.jar b/bitrock-installer/files/appengine-java-sdk/lib/user/appengine-jsr107cache-1.8.5.jar deleted file mode 100644 index fe206ba601..0000000000 --- a/bitrock-installer/files/appengine-java-sdk/lib/user/appengine-jsr107cache-1.8.5.jar +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2f72a4f76d29b6864e163df3ab6a770a2a90e8a2944f2bbcee7a9c07d617c82f -size 7064 diff --git a/bitrock-installer/files/appengine-java-sdk/lib/user/appengine-jsr107cache-1.9.0.jar b/bitrock-installer/files/appengine-java-sdk/lib/user/appengine-jsr107cache-1.9.0.jar new file mode 100644 index 0000000000..6fcfa4cdf4 --- /dev/null +++ b/bitrock-installer/files/appengine-java-sdk/lib/user/appengine-jsr107cache-1.9.0.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:be66451da694862a7f6f90de8e008b76b40d52442618be76bfd000e28f416840 +size 7131 diff --git a/bitrock-installer/files/appengine-java-sdk/lib/user/libservice.jar b/bitrock-installer/files/appengine-java-sdk/lib/user/libservice.jar new file mode 100644 index 0000000000..c01a6f73fe --- /dev/null +++ b/bitrock-installer/files/appengine-java-sdk/lib/user/libservice.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8aaa2790fb24df998dfecb7a38407bd28e66162d0402e8813ca12617ca1ff528 +size 1151 diff --git a/eclipse-aggregate-gae/.classpath b/eclipse-aggregate-gae/.classpath index bc76a67da0..905a88c582 100644 --- a/eclipse-aggregate-gae/.classpath +++ b/eclipse-aggregate-gae/.classpath @@ -24,13 +24,15 @@ - - - - - - - + + + + + + + + + @@ -41,16 +43,16 @@ - - + + - + + - @@ -59,7 +61,7 @@ - + @@ -83,14 +85,14 @@ - + + - @@ -103,6 +105,7 @@ - + + diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/google-api-client-1.16.0-rc.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/google-api-client-1.16.0-rc.jar deleted file mode 100644 index 451e2477c5..0000000000 --- a/eclipse-aggregate-gae/war/WEB-INF/lib/google-api-client-1.16.0-rc.jar +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5c2e8f0c68f60d32a1be65df8ddf1b0069d48517bec6e8c905e4c6cb49f5d603 -size 187082 diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/google-api-client-1.17.0-rc.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/google-api-client-1.17.0-rc.jar new file mode 100644 index 0000000000..f02b7cdce0 --- /dev/null +++ b/eclipse-aggregate-gae/war/WEB-INF/lib/google-api-client-1.17.0-rc.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:550675ed2e226ca5151218fb414c1c57d331af9508c37960b70b663ef5d3a9e9 +size 164057 diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/google-api-services-drive-v2-rev116-1.17.0-rc.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/google-api-services-drive-v2-rev116-1.17.0-rc.jar new file mode 100644 index 0000000000..5e50ab0965 --- /dev/null +++ b/eclipse-aggregate-gae/war/WEB-INF/lib/google-api-services-drive-v2-rev116-1.17.0-rc.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:08c146e051ba86b134a35240aae4f63135308b4d21341d0bfae1193779c13c03 +size 151304 diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/google-api-services-drive-v2-rev93-1.16.0-rc.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/google-api-services-drive-v2-rev93-1.16.0-rc.jar deleted file mode 100644 index 9d0b74c834..0000000000 --- a/eclipse-aggregate-gae/war/WEB-INF/lib/google-api-services-drive-v2-rev93-1.16.0-rc.jar +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:24078a632ea11a4eff40c3a54e2f688e4e5754e60b69cf61376ec6e0270f6033 -size 147278 diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/google-http-client-1.16.0-rc.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/google-http-client-1.16.0-rc.jar deleted file mode 100644 index c3b532b7cc..0000000000 --- a/eclipse-aggregate-gae/war/WEB-INF/lib/google-http-client-1.16.0-rc.jar +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:299b0982221a7e96962e9cccb8e4033a8b103b4a1b91bc664e5413d3e02f1aab -size 295218 diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/google-http-client-1.17.0-rc.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/google-http-client-1.17.0-rc.jar new file mode 100644 index 0000000000..e36a7d1b07 --- /dev/null +++ b/eclipse-aggregate-gae/war/WEB-INF/lib/google-http-client-1.17.0-rc.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c8e85f0d4882c012aeae02c954269eadb4b658b8db6e485b3d8241944b68ee0e +size 270099 diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/google-http-client-appengine-1.16.0-rc.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/google-http-client-appengine-1.16.0-rc.jar deleted file mode 100644 index 237f7371f2..0000000000 --- a/eclipse-aggregate-gae/war/WEB-INF/lib/google-http-client-appengine-1.16.0-rc.jar +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:706495cd7db350a914c624abbd0e158ce22f91d24e5b2156a91f664b4ca56b13 -size 15572 diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/google-http-client-appengine-1.17.0-rc.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/google-http-client-appengine-1.17.0-rc.jar new file mode 100644 index 0000000000..857c2537c0 --- /dev/null +++ b/eclipse-aggregate-gae/war/WEB-INF/lib/google-http-client-appengine-1.17.0-rc.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7d17548400b6c0a1da9f363ba634a50db932fc0777e35a162c2c6e616567f4ab +size 16339 diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/google-http-client-jackson-1.16.0-rc.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/google-http-client-jackson-1.16.0-rc.jar deleted file mode 100644 index aadb4bd18a..0000000000 --- a/eclipse-aggregate-gae/war/WEB-INF/lib/google-http-client-jackson-1.16.0-rc.jar +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5299eabff6f5bf59528c62535bb8b7b6d4a982e7cfee6653a57092102fc09044 -size 6037 diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/google-http-client-jackson-1.17.0-rc.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/google-http-client-jackson-1.17.0-rc.jar new file mode 100644 index 0000000000..07b58b477b --- /dev/null +++ b/eclipse-aggregate-gae/war/WEB-INF/lib/google-http-client-jackson-1.17.0-rc.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:69db77b45390524144c25d1386d5ce32e03543f0fd1e9c2d6f9aed83d804810e +size 6037 diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/google-http-client-jdo-1.17.0-rc.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/google-http-client-jdo-1.17.0-rc.jar new file mode 100644 index 0000000000..404469621b --- /dev/null +++ b/eclipse-aggregate-gae/war/WEB-INF/lib/google-http-client-jdo-1.17.0-rc.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7db7fbdc29ef40cf53aabff0e8c61063e17c99ae1e065ffab8e7c36c9463e178 +size 11726 diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/google-oauth-client-1.16.0-rc.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/google-oauth-client-1.16.0-rc.jar deleted file mode 100644 index e47b063b52..0000000000 --- a/eclipse-aggregate-gae/war/WEB-INF/lib/google-oauth-client-1.16.0-rc.jar +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e268bd493467ddf4239b7905001f78fa46fc07746292780a88cb1a2f9c2c9f94 -size 64819 diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/google-oauth-client-1.17.0-rc.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/google-oauth-client-1.17.0-rc.jar new file mode 100644 index 0000000000..07f6ce1701 --- /dev/null +++ b/eclipse-aggregate-gae/war/WEB-INF/lib/google-oauth-client-1.17.0-rc.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6f95c8bf356a96cf3a7f1bcc06f40d25416ea34be1e9666919ae84376b8e07ff +size 61049 diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/google-oauth-client-appengine-1.16.0-rc.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/google-oauth-client-appengine-1.16.0-rc.jar deleted file mode 100644 index 1f920c617e..0000000000 --- a/eclipse-aggregate-gae/war/WEB-INF/lib/google-oauth-client-appengine-1.16.0-rc.jar +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a4a76d59776d341dbf7f4e10c6ea5de804b596165f171d7d382180d956dff03b -size 9965 diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/google-oauth-client-appengine-1.17.0-rc.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/google-oauth-client-appengine-1.17.0-rc.jar new file mode 100644 index 0000000000..2a85d6bbee --- /dev/null +++ b/eclipse-aggregate-gae/war/WEB-INF/lib/google-oauth-client-appengine-1.17.0-rc.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ecac12858c59ad49debed906393b787fc57802ced77bb2318d5a1f1ebd42d935 +size 7947 diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/google-oauth-client-servlet-1.17.0-rc.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/google-oauth-client-servlet-1.17.0-rc.jar new file mode 100644 index 0000000000..772cf9f722 --- /dev/null +++ b/eclipse-aggregate-gae/war/WEB-INF/lib/google-oauth-client-servlet-1.17.0-rc.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:be7ff1da3049c6c8523bcd08b5dd6c8d4208796c694d276368e1bc4af0cc3cd0 +size 22107 diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/jackson-core-asl-1.9.11.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/jackson-core-asl-1.9.11.jar deleted file mode 100644 index df971df79b..0000000000 --- a/eclipse-aggregate-gae/war/WEB-INF/lib/jackson-core-asl-1.9.11.jar +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5fb6924b888550a9b0e8420747a93cc4ad24e03e724dcf4934c30cc0c4882ffc -size 232131 diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/jackson-core-asl-1.9.13.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/jackson-core-asl-1.9.13.jar new file mode 100644 index 0000000000..22d44506be --- /dev/null +++ b/eclipse-aggregate-gae/war/WEB-INF/lib/jackson-core-asl-1.9.13.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:440a9cb5ca95b215f953d3a20a6b1a10da1f09b529a9ddea5f8a4905ddab4f5a +size 232248 diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/jackson-mapper-asl-1.9.11.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/jackson-mapper-asl-1.9.11.jar deleted file mode 100644 index 53ad84a14d..0000000000 --- a/eclipse-aggregate-gae/war/WEB-INF/lib/jackson-mapper-asl-1.9.11.jar +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:246ee4dcb26cb040608eab5d978efe2618564568923c0a98e6118f8858b31def -size 780421 diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/jackson-mapper-asl-1.9.13.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/jackson-mapper-asl-1.9.13.jar new file mode 100644 index 0000000000..51fe52924d --- /dev/null +++ b/eclipse-aggregate-gae/war/WEB-INF/lib/jackson-mapper-asl-1.9.13.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:74e7a07a76f2edbade29312a5a2ebccfa019128bc021ece3856d76197e9be0c2 +size 780664 diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/jaxrs-api-2.3.3.Final.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/jaxrs-api-2.3.3.Final.jar deleted file mode 100644 index a16eac951c..0000000000 --- a/eclipse-aggregate-gae/war/WEB-INF/lib/jaxrs-api-2.3.3.Final.jar +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4f68f6a8dae8b426fc3b324867f26243c341d63fdfe86cc21c652e360916240c -size 46448 diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/jaxrs-api-3.0.6.Final.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/jaxrs-api-3.0.6.Final.jar new file mode 100644 index 0000000000..feeb137980 --- /dev/null +++ b/eclipse-aggregate-gae/war/WEB-INF/lib/jaxrs-api-3.0.6.Final.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3ab50ce11ea1a04a4a03aaed31d9335fa170d993d6c62027d780c9f1c33b2d92 +size 107124 diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/jboss-annotations-api_1.1_spec-1.0.1.Final.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/jboss-annotations-api_1.1_spec-1.0.1.Final.jar new file mode 100644 index 0000000000..6cb770bdd6 --- /dev/null +++ b/eclipse-aggregate-gae/war/WEB-INF/lib/jboss-annotations-api_1.1_spec-1.0.1.Final.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b9bca3580b231d22864c089da17cb6c3b1bdc92971c03abe2235324cc06da11e +size 18778 diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/jdom-1.1.2.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/jdom-1.1.2.jar deleted file mode 100644 index 9e28342b6a..0000000000 --- a/eclipse-aggregate-gae/war/WEB-INF/lib/jdom-1.1.2.jar +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2bf22cb2258271c99888df76e1a09cec7938b4d12dd9b2e8aaecdc83e172c6c8 -size 152526 diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/jsr250-api-1.0.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/jsr250-api-1.0.jar deleted file mode 100644 index 10d77d0014..0000000000 --- a/eclipse-aggregate-gae/war/WEB-INF/lib/jsr250-api-1.0.jar +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d869826b0001ee5c0690344850d0897e929e5ef17fa14c8bb454bc49b3406dbd -size 6165 diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/stax-api-1.0-2.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/stax-api-1.0-2.jar deleted file mode 100644 index 950ede761f..0000000000 --- a/eclipse-aggregate-gae/war/WEB-INF/lib/stax-api-1.0-2.jar +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e8c70ebd76f982c9582a82ef82cf6ce14a7d58a4a4dca5cb7b7fc988c80089b7 -size 23346 diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/stax-api-1.0.1.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/stax-api-1.0.1.jar new file mode 100644 index 0000000000..4869277c16 --- /dev/null +++ b/eclipse-aggregate-gae/war/WEB-INF/lib/stax-api-1.0.1.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d1968436fc216c901fb9b82c7e878b50fd1d30091676da95b2edd3a9c0ccf92e +size 26514 diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/transaction-api-1.1.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/transaction-api-1.1.jar new file mode 100644 index 0000000000..6aa2c4430c --- /dev/null +++ b/eclipse-aggregate-gae/war/WEB-INF/lib/transaction-api-1.1.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b8ec163b4a47bad16f9a0b7d03c3210c6b0a29216d768031073ac20817c0ba50 +size 15071 diff --git a/eclipse-aggregate-mysql/.classpath b/eclipse-aggregate-mysql/.classpath index 45e84ffab4..9add8fb74f 100644 --- a/eclipse-aggregate-mysql/.classpath +++ b/eclipse-aggregate-mysql/.classpath @@ -24,13 +24,15 @@ - - - - - - - + + + + + + + + + @@ -41,16 +43,16 @@ - - + + - + + - @@ -83,7 +85,8 @@ - + + diff --git a/pom.xml b/pom.xml index 06a09db293..077eec5a38 100644 --- a/pom.xml +++ b/pom.xml @@ -17,30 +17,29 @@ ${project.basedir}/../src/it/testfiles/forms ${project.basedir}/../src/it/testfiles/submissions UTF-8 - - 1.8.0 + 1.9.0 ${gooogle.appengine.sdk.version} 1.47.1 - 1.16.0-rc - v2-rev91-1.16.0-rc + 1.17.0-rc + v2-rev116-1.17.0-rc 2.5.1 3.0.6.Final 1.2 - 2.12.4 - 2.12.4 + 2.16 + 2.16 -Xms300m -Xmx3048m -XX:+UseParallelGC -XX:+UseParallelOldGC - 2.12.4 - 1.3.1 - 1.7 + 2.16 + 1.4.7 + 1.8 2.5.1 0.9.6 2.4 2.9.1 - 2.3 + 2.4 1.7 - 3.0 + 3.1 4.11 - 1.9.12 + 1.9.13 3.1.3.RELEASE 1.6.6 @@ -84,6 +83,16 @@ central + Maven Central Repository + default + http://repo.maven.apache.org/maven2 + + false + + + + + old central Maven Repository Switchboard default http://repo1.maven.org/maven2 @@ -91,13 +100,13 @@ false - + @@ -120,7 +129,7 @@ codehaus-snapshots - https://nexus.codehaus.org/content/repositories/snapshots/ + https://nexus.codehaus.org/snapshots/ true daily @@ -144,16 +153,32 @@ central + Maven Central Repository + default + http://repo.maven.apache.org/maven2 + + false + + + never + + + + + old central Maven Repository Switchboard default http://repo1.maven.org/maven2 false + + never + codehaus-snapshots - https://nexus.codehaus.org/content/repositories/snapshots/ + https://nexus.codehaus.org/snapshots/ true daily @@ -177,12 +202,6 @@ plugins-snapshot https://repo.springsource.org/plugins-snapshot - - - maven-gae-plugin-repo - Maven Google App Engine Repository - http://maven-gae-plugin.googlecode.com/svn/repository/ - @@ -522,7 +541,7 @@ org.javarosa javarosa-libraries - 2014-03-07 + 2014-03-10 jar compile @@ -972,7 +991,7 @@ org.apache.maven.plugins maven-eclipse-plugin - 2.8 + 2.9 ${project.basedir}/build/webapp @@ -980,7 +999,7 @@ org.apache.maven.plugins maven-dependency-plugin - 2.4 + 2.8 diff --git a/src/main/java/org/opendatakit/aggregate/constants/common/UIConsts.java b/src/main/java/org/opendatakit/aggregate/constants/common/UIConsts.java index 42a725087f..3d7d41bbf6 100644 --- a/src/main/java/org/opendatakit/aggregate/constants/common/UIConsts.java +++ b/src/main/java/org/opendatakit/aggregate/constants/common/UIConsts.java @@ -17,7 +17,7 @@ package org.opendatakit.aggregate.constants.common; public class UIConsts { - public static final String VERSION_STRING = "v1.4.2 Production"; + public static final String VERSION_STRING = "v1.4.3 Production"; public static final String URI_DEFAULT = "no uuid"; public static final String FSC_URI_PARAM = "fsc"; diff --git a/src/main/java/org/opendatakit/aggregate/odktables/impl/api/AppEngineContentEncodingSuppressionRequestFilter.java b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/AppEngineContentEncodingSuppressionRequestFilter.java new file mode 100644 index 0000000000..b2460cb855 --- /dev/null +++ b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/AppEngineContentEncodingSuppressionRequestFilter.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2014 University of Washington + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.opendatakit.aggregate.odktables.impl.api; + +import java.io.IOException; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.ConstrainedTo; +import javax.ws.rs.RuntimeType; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.container.PreMatching; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MultivaluedMap; + +import org.opendatakit.aggregate.ContextFactory; +import org.opendatakit.aggregate.odktables.rest.ApiConstants; +import org.opendatakit.common.web.CallingContext; + +/** + * AppEngine does automatic GZIP stream processing as part of its supplied + * framework. However, it does not remove the headers indicating that this has + * occurred. So the RestEasy code tries to double-unzip or double-zip the + * content. + * + * This filter works around that by removing the Content-Encoding header before + * the request gets to the RestEasy layers. + * + * NOTE: behavior has changed between 1.8.9 and 1.9.0 on development server. + * Beware! + * + * @author mitchellsundt@gmail.com + * + */ +@PreMatching +@ConstrainedTo(RuntimeType.SERVER) +public class AppEngineContentEncodingSuppressionRequestFilter implements ContainerRequestFilter { + + @Context + ServletContext sc; + @Context + HttpServletRequest req; + + @Override + public void filter(ContainerRequestContext crc) throws IOException { + CallingContext cc = ContextFactory.getCallingContext(sc, req); + String server = sc.getServerInfo(); + boolean isGaeDevelopmentEnvironment = server.contains("Development"); + boolean isGaeEnvironment = cc.getUserService().getCurrentRealm().getIsGaeEnvironment(); + if (isGaeEnvironment && !isGaeDevelopmentEnvironment) { + MultivaluedMap headers = crc.getHeaders(); + headers.remove(ApiConstants.CONTENT_ENCODING_HEADER); + } + } + +} diff --git a/src/main/java/org/opendatakit/aggregate/odktables/impl/api/AppEngineContentEncodingSuppressionResponseFilter.java b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/AppEngineContentEncodingSuppressionResponseFilter.java new file mode 100644 index 0000000000..5a37722937 --- /dev/null +++ b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/AppEngineContentEncodingSuppressionResponseFilter.java @@ -0,0 +1,70 @@ +package org.opendatakit.aggregate.odktables.impl.api; + +import java.io.IOException; + +import javax.annotation.Priority; +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.ConstrainedTo; +import javax.ws.rs.RuntimeType; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.WriterInterceptor; +import javax.ws.rs.ext.WriterInterceptorContext; + +import org.opendatakit.aggregate.ContextFactory; +import org.opendatakit.aggregate.odktables.rest.ApiConstants; +import org.opendatakit.common.web.CallingContext; + +/** + * AppEngine does automatic GZIP stream processing as part of its supplied + * framework. Resteasy does the same; left on their own, response entities would + * be doubly-GZIPped. + * + * This class suppresses the Resteasy behavior by removing the Content-Encoding + * heading that the Resteasy framework adds before its GZIP processing tests for + * that header and GZIPs the response entity. + * + * The net effect is that the entity is not compressed by the Resteasy + * framework, and is passed un-compressed up to the Google layer. + * + * On Google's production servers, the response is then compressed. + * + * IMPORTANT: The Google development server does not compress responses (but, if + * we were to allow the Resteasy framework to compress them, the development + * server would strip out the Content-Encoding header before returning the data + * to the client). + * + * NOTE: the Priority attribute must be a value between 3000 (when the + * Content-Encoding header is added by Resteasy) and 4000 (when the GZIP + * processing is performed). + * + * @author mitchellsundt@gmail.com + * + */ +@ConstrainedTo(RuntimeType.SERVER) +@Priority(3500) +public class AppEngineContentEncodingSuppressionResponseFilter implements WriterInterceptor { + + @Context + ServletContext sc; + @Context + HttpServletRequest req; + + @Override + public void aroundWriteTo(WriterInterceptorContext context) throws IOException, + WebApplicationException { + CallingContext cc = ContextFactory.getCallingContext(sc, req); + String server = sc.getServerInfo(); + @SuppressWarnings("unused") + boolean isGaeDevelopmentEnvironment = server.contains("Development"); + boolean suppressZipping = cc.getUserService().getCurrentRealm().getIsGaeEnvironment(); + if (suppressZipping) { + MultivaluedMap headers = context.getHeaders(); + headers.remove(ApiConstants.CONTENT_ENCODING_HEADER); + } + context.proceed(); + } + +} diff --git a/src/main/java/org/opendatakit/aggregate/odktables/impl/api/FileManifestServiceImpl.java b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/FileManifestServiceImpl.java index 9abdf38b69..64aa0e2ae9 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/impl/api/FileManifestServiceImpl.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/FileManifestServiceImpl.java @@ -21,6 +21,7 @@ import javax.ws.rs.PathParam; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.UriInfo; @@ -51,9 +52,9 @@ public class FileManifestServiceImpl implements FileManifestService { private TablesUserPermissions userPermissions; private UriInfo info; - public FileManifestServiceImpl(@Context ServletContext sc, @Context HttpServletRequest req, + public FileManifestServiceImpl(@Context ServletContext sc, @Context HttpServletRequest req, @Context HttpHeaders httpHeaders, @Context UriInfo info) throws ODKDatastoreException, PermissionDeniedException, ODKTaskLockException { - ServiceUtils.examineRequest(sc, req); + ServiceUtils.examineRequest(sc, req, httpHeaders); this.cc = ContextFactory.getCallingContext(sc, req); this.userPermissions = new TablesUserPermissionsImpl(this.cc.getCurrentUser().getUriUser(), cc); this.info = info; diff --git a/src/main/java/org/opendatakit/aggregate/odktables/impl/api/FileServiceImpl.java b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/FileServiceImpl.java index 65ecd33151..3edcf95c43 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/impl/api/FileServiceImpl.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/FileServiceImpl.java @@ -27,6 +27,7 @@ import javax.ws.rs.PathParam; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.PathSegment; import javax.ws.rs.core.Response; @@ -79,9 +80,9 @@ public class FileServiceImpl implements FileService { private TablesUserPermissions userPermissions; private UriInfo info; - public FileServiceImpl(@Context ServletContext sc, @Context HttpServletRequest req, + public FileServiceImpl(@Context ServletContext sc, @Context HttpServletRequest req, @Context HttpHeaders httpHeaders, @Context UriInfo info) throws ODKDatastoreException, PermissionDeniedException, ODKTaskLockException { - ServiceUtils.examineRequest(sc, req); + ServiceUtils.examineRequest(sc, req, httpHeaders); this.cc = ContextFactory.getCallingContext(sc, req); this.userPermissions = new TablesUserPermissionsImpl(this.cc.getCurrentUser().getUriUser(), cc); this.info = info; diff --git a/src/main/java/org/opendatakit/aggregate/odktables/impl/api/IOExceptionMapper.java b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/IOExceptionMapper.java index 8d9ec3d5e5..c2f4699be7 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/impl/api/IOExceptionMapper.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/IOExceptionMapper.java @@ -38,6 +38,7 @@ public class IOExceptionMapper implements ExceptionMapper { @Override public Response toResponse(IOException e) { MediaType type; + e.printStackTrace(); type = (headers.getAcceptableMediaTypes().size() != 0) ? headers.getAcceptableMediaTypes().get( 0) : MediaType.APPLICATION_JSON_TYPE; diff --git a/src/main/java/org/opendatakit/aggregate/odktables/impl/api/ODKDatastoreExceptionMapper.java b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/ODKDatastoreExceptionMapper.java index bfdc8f2000..a0a9408894 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/impl/api/ODKDatastoreExceptionMapper.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/ODKDatastoreExceptionMapper.java @@ -40,6 +40,7 @@ public class ODKDatastoreExceptionMapper implements ExceptionMapper> getClasses() { classes.add(ODKTaskLockExceptionMapper.class); classes.add(IOExceptionMapper.class); classes.add(RuntimeExceptionMapper.class); + classes.add(AppEngineContentEncodingSuppressionRequestFilter.class); + classes.add(AppEngineContentEncodingSuppressionResponseFilter.class); return classes; } } \ No newline at end of file diff --git a/src/main/java/org/opendatakit/aggregate/odktables/impl/api/ODKTablesExceptionMapper.java b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/ODKTablesExceptionMapper.java index 6582034779..3cee742c5d 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/impl/api/ODKTablesExceptionMapper.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/ODKTablesExceptionMapper.java @@ -42,6 +42,7 @@ public class ODKTablesExceptionMapper implements ExceptionMapper @Override public Response toResponse(RuntimeException e) { MediaType type; + e.printStackTrace(); type = (headers.getAcceptableMediaTypes().size() != 0) ? headers.getAcceptableMediaTypes().get( 0) : MediaType.APPLICATION_JSON_TYPE; diff --git a/src/main/java/org/opendatakit/aggregate/odktables/impl/api/ServiceUtils.java b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/ServiceUtils.java index 9f85053bfe..dc224061f9 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/impl/api/ServiceUtils.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/ServiceUtils.java @@ -16,10 +16,13 @@ package org.opendatakit.aggregate.odktables.impl.api; import java.util.Enumeration; +import java.util.List; import javax.servlet.ServletContext; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MultivaluedMap; import org.opendatakit.aggregate.odktables.rest.ApiConstants; @@ -58,4 +61,25 @@ public static void examineRequest(ServletContext sc, HttpServletRequest req) { String ace = req.getHeader(ApiConstants.ACCEPT_CONTENT_ENCODING_HEADER); boolean sessionId = req.isRequestedSessionIdValid(); } + + @SuppressWarnings("unused") + public static void examineRequest(ServletContext sc, HttpServletRequest req, HttpHeaders httpHeaders) { + MultivaluedMap headers = httpHeaders.getRequestHeaders(); + StringBuilder b = new StringBuilder(); + for ( String headerName : headers.keySet() ) { + List fieldValues = headers.get(headerName); + for (String fieldValue : fieldValues) { + b.append(headerName).append(": ").append(fieldValue).append("\n"); + } + } + String contentType = req.getContentType(); + String charEncoding = req.getCharacterEncoding(); + String headerSet = b.toString(); + Cookie[] cookies = req.getCookies(); + String method = req.getMethod(); + String ctxtPath = req.getContextPath(); + String pathInfo = req.getPathInfo(); + String query = req.getQueryString(); + boolean sessionId = req.isRequestedSessionIdValid(); + } } diff --git a/src/main/java/org/opendatakit/aggregate/odktables/impl/api/TableServiceImpl.java b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/TableServiceImpl.java index 9949ac1844..c8d784cfaa 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/impl/api/TableServiceImpl.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/TableServiceImpl.java @@ -24,6 +24,7 @@ import javax.servlet.http.HttpServletRequest; import javax.ws.rs.PathParam; import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; @@ -62,9 +63,9 @@ public class TableServiceImpl implements TableService { private TableManager tm; private UriInfo info; - public TableServiceImpl(@Context ServletContext sc, @Context HttpServletRequest req, + public TableServiceImpl(@Context ServletContext sc, @Context HttpServletRequest req, @Context HttpHeaders httpHeaders, @Context UriInfo info) throws ODKDatastoreException, PermissionDeniedException, ODKTaskLockException { - ServiceUtils.examineRequest(sc, req); + ServiceUtils.examineRequest(sc, req, httpHeaders); this.cc = ContextFactory.getCallingContext(sc, req); this.userPermissions = new TablesUserPermissionsImpl(this.cc.getCurrentUser().getUriUser(), cc); String appId = ServerPreferencesProperties.getOdkTablesAppId(cc); diff --git a/src/main/java/org/opendatakit/aggregate/parser/BaseFormParserForJavaRosa.java b/src/main/java/org/opendatakit/aggregate/parser/BaseFormParserForJavaRosa.java index e1b8526962..2667c659ec 100644 --- a/src/main/java/org/opendatakit/aggregate/parser/BaseFormParserForJavaRosa.java +++ b/src/main/java/org/opendatakit/aggregate/parser/BaseFormParserForJavaRosa.java @@ -264,7 +264,7 @@ public XFormParserWithBindEnhancements(BaseFormParserForJavaRosa parser, Documen protected void parseBind(Element e) { // remember raw bindings in case we want to compare parsed XForms later parser.bindElements.addElement(copyBindingElement(e)); - Vector usedAtts = new Vector(); + Vector usedAtts = new Vector(); DataBinding binding = processStandardBindAttributes(usedAtts, e); @@ -339,7 +339,7 @@ private void setNodesetStringLength(String nodeset, Integer length) { stringLengths.put(nodeset, length); } - protected Integer getNodesetStringLength(AbstractTreeElement e) { + protected Integer getNodesetStringLength(AbstractTreeElement e) { List path = new ArrayList(); while (e != null && e.getName() != null) { path.add(e.getName()); @@ -664,7 +664,7 @@ private void printTreeElementInfo(TreeElement treeElement) { + " repeatable: " + treeElement.isRepeatable()); } - public String getTreeElementPath(AbstractTreeElement e) { + public String getTreeElementPath(AbstractTreeElement e) { if (e == null) return null; String s = getTreeElementPath(e.getParent()); @@ -793,9 +793,9 @@ public static DifferenceResult compareXml(BaseFormParserForJavaRosa incomingPars // that will be submitted to Aggregate. IDataReference r; r = subProfile1.getRef(); - AbstractTreeElement e1 = (r != null) ? formDef1.getInstance().resolveReference(r) : null; + AbstractTreeElement e1 = (r != null) ? formDef1.getInstance().resolveReference(r) : null; r = subProfile2.getRef(); - AbstractTreeElement e2 = (r != null) ? formDef2.getInstance().resolveReference(r) : null; + AbstractTreeElement e2 = (r != null) ? formDef2.getInstance().resolveReference(r) : null; if (e1 != null && e2 != null) { // both return only a portion of the form. @@ -1014,7 +1014,8 @@ public static DifferenceResult compareTreeElements(TreeElement treeElement1, // Otherwise, if you have specified the jr:template attribute, // it appears only as an INDEX_TEMPLATE element. - int template1DropCount = 0; + @SuppressWarnings("unused") + int template1DropCount = 0; // get non-template entries for treeElement1 List element1ExcludingRepeatIndex0Children = new ArrayList(); @@ -1031,7 +1032,8 @@ public static DifferenceResult compareTreeElements(TreeElement treeElement1, element1ExcludingRepeatIndex0Children.add(child); } - int template2DropCount = 0; + @SuppressWarnings("unused") + int template2DropCount = 0; // get non-template entries for treeElement2 Map element2ExcludingRepeatIndex0Children = new HashMap(); diff --git a/src/main/java/org/opendatakit/common/security/Realm.java b/src/main/java/org/opendatakit/common/security/Realm.java index a39f315dfc..29745967a0 100644 --- a/src/main/java/org/opendatakit/common/security/Realm.java +++ b/src/main/java/org/opendatakit/common/security/Realm.java @@ -22,12 +22,12 @@ /** * A bean class used to capture configuration values about this server - * deployment, its default mailto: domain and the service domains it + * deployment, its default mailto: domain and the service domains it * authorizes. - * + * * @author wbrunette@gmail.com * @author mitchellsundt@gmail.com - * + * */ public class Realm implements InitializingBean { @@ -37,7 +37,8 @@ public class Realm implements InitializingBean { private Integer securePort; private String hostname; private String realmString; - + private boolean isGaeEnvironment = false; + public Realm() { } @@ -46,7 +47,6 @@ public void afterPropertiesSet() throws Exception { if ( realmString == null ) { throw new IllegalStateException("realmString (e.g., mydomain.org ODK Aggregate 1.0) must be specified"); } - Log log = LogFactory.getLog(Realm.class); log.info("Version: " + UIConsts.VERSION_STRING); log.info("Hostname: " + hostname); @@ -55,24 +55,25 @@ public void afterPropertiesSet() throws Exception { log.info("SslIsRequired: " + (sslIsRequired ? "yes" : "no") ); log.info("SslIsAvailable: " + (sslIsAvailable ? "yes" : "no") ); log.info("RealmString: " + realmString); + log.info("isGaeEnvironment: " + (isGaeEnvironment ? "yes" : "no") ); } - + public void setSecureChannelType(String type) { if ( type != null && type.equals("REQUIRES_SECURE_CHANNEL") ) { sslIsAvailable = true; } } - + public boolean isSslAvailable() { return sslIsAvailable; } - + public void setChannelType(String type) { if ( type != null && type.equals("REQUIRES_SECURE_CHANNEL") ) { sslIsRequired = true; } } - + public boolean isSslRequired() { return sslIsRequired; } @@ -108,4 +109,13 @@ public String getRealmString() { public void setRealmString(String realmString) { this.realmString = realmString; } + + public boolean getIsGaeEnvironment() { + return isGaeEnvironment; + } + + public void setIsGaeEnvironment(boolean isGaeEnvironment) { + this.isGaeEnvironment = isGaeEnvironment; + } + } diff --git a/src/main/resources/common/security.properties b/src/main/resources/common/security.properties index c5d024c540..adea58a322 100644 --- a/src/main/resources/common/security.properties +++ b/src/main/resources/common/security.properties @@ -26,7 +26,7 @@ security.server.channelType=REQUIRES_INSECURE_CHANNEL # # This is configured during install. If blank, discovers an IP address #security.server.hostname= -security.server.hostname=sprite-1.dyn.cs.washington.edu +security.server.hostname=sprite.cs.washington.edu # # any port pairings can be used. #security.server.port=80 diff --git a/src/main/resources/gae-unit/odk-settings.xml b/src/main/resources/gae-unit/odk-settings.xml index 90c7c7d575..d1897e3a07 100644 --- a/src/main/resources/gae-unit/odk-settings.xml +++ b/src/main/resources/gae-unit/odk-settings.xml @@ -22,6 +22,7 @@ + diff --git a/src/main/resources/gae/odk-settings.xml b/src/main/resources/gae/odk-settings.xml index 254f3926af..a69ce424c5 100644 --- a/src/main/resources/gae/odk-settings.xml +++ b/src/main/resources/gae/odk-settings.xml @@ -20,6 +20,7 @@ + diff --git a/src/main/resources/mysql-unit/odk-settings.xml b/src/main/resources/mysql-unit/odk-settings.xml index b06c8fe54b..b874e2485e 100644 --- a/src/main/resources/mysql-unit/odk-settings.xml +++ b/src/main/resources/mysql-unit/odk-settings.xml @@ -41,6 +41,7 @@ + diff --git a/src/main/resources/mysql/odk-settings.xml b/src/main/resources/mysql/odk-settings.xml index b8b952cd92..c35ed041d8 100644 --- a/src/main/resources/mysql/odk-settings.xml +++ b/src/main/resources/mysql/odk-settings.xml @@ -41,6 +41,7 @@ + diff --git a/src/main/resources/postgres-unit/odk-settings.xml b/src/main/resources/postgres-unit/odk-settings.xml index b0d5d64939..dc1041d7e7 100644 --- a/src/main/resources/postgres-unit/odk-settings.xml +++ b/src/main/resources/postgres-unit/odk-settings.xml @@ -41,6 +41,7 @@ + diff --git a/src/main/resources/postgres/odk-settings.xml b/src/main/resources/postgres/odk-settings.xml index b0d5d64939..dc1041d7e7 100644 --- a/src/main/resources/postgres/odk-settings.xml +++ b/src/main/resources/postgres/odk-settings.xml @@ -41,6 +41,7 @@ + diff --git a/src/test/java/org/opendatakit/aggregate/odktables/DataManagerTest.java b/src/test/java/org/opendatakit/aggregate/odktables/DataManagerTest.java index 289fff6777..a13e9e7706 100644 --- a/src/test/java/org/opendatakit/aggregate/odktables/DataManagerTest.java +++ b/src/test/java/org/opendatakit/aggregate/odktables/DataManagerTest.java @@ -326,7 +326,6 @@ public void testUpdateRowBadColumnName() throws ODKEntityPersistException, @Test public void testDelete2Rows() throws ODKEntityPersistException, ODKDatastoreException, ODKTaskLockException, BadColumnNameException, ETagMismatchException, PermissionDeniedException, InconsistentStateException { - @SuppressWarnings("unused") List expectedChanges = new ArrayList(); for ( Row r : rows ) { expectedChanges.add(dm.insertOrUpdateRow(r)); diff --git a/src/test/java/org/opendatakit/aggregate/odktables/TableManagerTest.java b/src/test/java/org/opendatakit/aggregate/odktables/TableManagerTest.java index bde170788c..011fdfd342 100644 --- a/src/test/java/org/opendatakit/aggregate/odktables/TableManagerTest.java +++ b/src/test/java/org/opendatakit/aggregate/odktables/TableManagerTest.java @@ -55,7 +55,9 @@ public class TableManagerTest { private TableManager tm; private String tableId; private String tableId2; + @SuppressWarnings("unused") private List columns; + @SuppressWarnings("unused") private String tableProperties; private class MockCurrentUserPermissions implements TablesUserPermissions { @@ -237,8 +239,10 @@ public void testGetTablesByScopes() throws ODKEntityNotFoundException, ODKDatast pm.setProperties(tableProperties); TableEntry one = tm.getTable(tableId); - TableEntry entry2 = tm.createTable(tableId2, T.columns); + @SuppressWarnings("unused") + TableEntry entry2 = tm.createTable(tableId2, T.columns); PropertiesManager pm2 = new PropertiesManager( T.appId, tableId2, userPermissions, cc); + @SuppressWarnings("unused") TableProperties tableProperties2 = new TableProperties(entry.getSchemaETag(), T.propertiesETag, tableId2, T.kvsEntries); pm2.setProperties(tableProperties); tm.createTable(tableId2, T.columns); From 4353431b912c655caa94b93b8dea8777d01b87a5 Mon Sep 17 00:00:00 2001 From: mitchellsundt Date: Mon, 21 Apr 2014 08:49:43 -0700 Subject: [PATCH 05/13] Rename PRIME_COLS to GROUP_BY_COLS, add SORT_ORDER. Remove table type, sms, access control, current query, view type and summary display formatting fields. --- .../rest/KeyValueStoreConstants.java | 40 ++++++++++++++----- .../aggregate/server/ServerOdkTablesUtil.java | 30 ++------------ .../opendatakit/aggregate/odktables/T.java | 28 ------------- 3 files changed, 33 insertions(+), 65 deletions(-) diff --git a/src/main/java/org/opendatakit/aggregate/odktables/rest/KeyValueStoreConstants.java b/src/main/java/org/opendatakit/aggregate/odktables/rest/KeyValueStoreConstants.java index 1b106540fa..7774be34de 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/rest/KeyValueStoreConstants.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/rest/KeyValueStoreConstants.java @@ -34,27 +34,47 @@ public class KeyValueStoreConstants { // Keys for: // TABLE_PARTITION, DEFAULT_ASPECT... - public static final String TABLE_TYPE = "tableType"; - public static final String TABLE_ACCESS_CONTROL_TABLE_ID = "accessControlTableId"; + + // required for ODK Aggregate displays + // json holding a string or a map of (locale -> string) public static final String TABLE_DISPLAY_NAME = "displayName"; + + // These fields are useful for the spreadsheet view of the data + // we may want to use them on ODK Aggregate. + + // json serialization of the list of elementKeys to display public static final String TABLE_COL_ORDER = "colOrder"; - public static final String TABLE_PRIME_COLS = "primeCols"; + // json serialization of the list of elementKeys to group by + public static final String TABLE_GROUP_BY_COLS = "groupByCols"; + // elementKey of the column to sort by public static final String TABLE_SORT_COL = "sortCol"; + // true if sort order is ascending + public static final String TABLE_SORT_ORDER = "sortOrder"; + // elementKey held fixed during left/right pan public static final String TABLE_INDEX_COL = "indexCol"; - public static final String TABLE_CURRENT_VIEW_TYPE = "currentViewType"; - public static final String TABLE_SUMMARY_DISPLAY_FORMAT = "summaryDisplayFormat"; - public static final String TABLE_CURRENT_QUERY = "currentQuery"; // Keys for: // COLUMN_PARTITION, dbColName... + + // These may also be useful for the spreadsheet view of the data + // we may want to use them on ODK Aggregate. + + // json holding a string or a map of (locale -> string) public static final String COLUMN_DISPLAY_NAME = "displayName"; + + // boolean value (visible/hidden) public static final String COLUMN_DISPLAY_VISIBLE = "displayVisible"; + + // json holding a list of maps with 'data_value' and 'display', a nested + // map that holds all the display properties, including 'text' which can be + // internationalized. public static final String COLUMN_DISPLAY_CHOICES_LIST = "displayChoicesList"; + + // json holding a string or a map of (locale -> string) + // this is used to render the data value (esp. for composite data values) public static final String COLUMN_DISPLAY_FORMAT = "displayFormat"; - public static final String COLUMN_SMS_IN = "smsIn"; - public static final String COLUMN_SMS_OUT = "smsOut"; - public static final String COLUMN_SMS_LABEL = "smsLabel"; - public static final String COLUMN_FOOTER_MODE = "footerMode"; + + // json -- format TBD public static final String COLUMN_JOINS = "joins"; } diff --git a/src/main/java/org/opendatakit/aggregate/server/ServerOdkTablesUtil.java b/src/main/java/org/opendatakit/aggregate/server/ServerOdkTablesUtil.java index fcd8e950c0..65a3e14ba2 100644 --- a/src/main/java/org/opendatakit/aggregate/server/ServerOdkTablesUtil.java +++ b/src/main/java/org/opendatakit/aggregate/server/ServerOdkTablesUtil.java @@ -106,18 +106,8 @@ public static TableEntryClient createTable(String appId, String tableId, TableDe PropertiesManager pm = new PropertiesManager(appId, tableId, userPermissions, cc); TableProperties tableProperties = pm.getProperties(); // TODO: - // (1) add table type (Data) - // (2) add displayName (displayName) + // (1) add displayName (displayName) // - OdkTablesKeyValueStoreEntry tt; - tt = new OdkTablesKeyValueStoreEntry(); - tt.tableId = tableId; - tt.partition = KeyValueStoreConstants.PARTITION_TABLE; - tt.aspect = KeyValueStoreConstants.ASPECT_DEFAULT; - tt.key = KeyValueStoreConstants.TABLE_TYPE; - tt.type = "string"; - tt.value = type.name(); - OdkTablesKeyValueStoreEntry tn; tn = new OdkTablesKeyValueStoreEntry(); tn.tableId = tableId; @@ -127,22 +117,11 @@ public static TableEntryClient createTable(String appId, String tableId, TableDe tn.type = "json"; tn.value = displayName; - boolean foundTT = false; boolean foundTN = false; - boolean changedTT = false; boolean changedTN = false; ArrayList kvsEntries = tableProperties.getKeyValueStoreEntries(); for ( OdkTablesKeyValueStoreEntry kvs : kvsEntries ) { - if ( kvs.key == tt.key && kvs.aspect == tt.aspect && kvs.partition == tt.partition ) { - foundTT = true; - if ( tt.type.equals(kvs.type) && type.name().equals(kvs.value) ) { - changedTT = false; - } else { - kvs.type = tt.type; - kvs.value = type.name(); - changedTT = true; - } - } else if ( kvs.key == tn.key && kvs.aspect == tn.aspect && kvs.partition == tn.partition ) { + if ( kvs.key == tn.key && kvs.aspect == tn.aspect && kvs.partition == tn.partition ) { foundTN = true; if ( tn.type.equals(kvs.type) && displayName.equals(kvs.value) ) { changedTN = false; @@ -153,13 +132,10 @@ public static TableEntryClient createTable(String appId, String tableId, TableDe } } } - if ( !foundTT ) { - kvsEntries.add(tt); - } if ( !foundTN ) { kvsEntries.add(tn); } - if ( !foundTT || !foundTN || changedTT || changedTN ) { + if ( !foundTN || changedTN ) { tableProperties.setKeyValueStoreEntries(kvsEntries); pm.setProperties(tableProperties); } diff --git a/src/test/java/org/opendatakit/aggregate/odktables/T.java b/src/test/java/org/opendatakit/aggregate/odktables/T.java index ae5fd51e1b..af811ed246 100644 --- a/src/test/java/org/opendatakit/aggregate/odktables/T.java +++ b/src/test/java/org/opendatakit/aggregate/odktables/T.java @@ -35,9 +35,7 @@ public class T { public static class OdkTablesKeyValueStoreEntries { public static final OdkTablesKeyValueStoreEntry entryOne; public static final OdkTablesKeyValueStoreEntry entryTwo; - public static final OdkTablesKeyValueStoreEntry tableType; public static final OdkTablesKeyValueStoreEntry displayName; - public static final OdkTablesKeyValueStoreEntry tableAccessControls; public static final String entryOnePartition = "tablePartition"; public static final String entryOneAspect = "tableAspect"; @@ -57,14 +55,6 @@ public static class OdkTablesKeyValueStoreEntries { public static final String displayNameType = "object"; public static final String displayNameValue = "\"People\""; - public static final String tableTypeKey = KeyValueStoreConstants.TABLE_TYPE; - public static final String tableTypeType = "string"; - public static final TableType tableTypeValueAsTableType = TableType.DATA; - public static final String tableTypeValue = tableTypeValueAsTableType.name(); - - public static final String tableAccessControlTableIdKey = KeyValueStoreConstants.TABLE_ACCESS_CONTROL_TABLE_ID; - public static final String tableAccessControlTableIdType = "string"; - public static final String tableAccessControlTableIdValue = "someId"; static { entryOne = new OdkTablesKeyValueStoreEntry(); entryOne.tableId = T.tableId; @@ -82,14 +72,6 @@ public static class OdkTablesKeyValueStoreEntries { entryTwo.type = entryTwoType; entryTwo.value = entryTwoValue; - tableType = new OdkTablesKeyValueStoreEntry(); - tableType.tableId = T.tableId; - tableType.partition = tablePartition; - tableType.aspect = tableAspect; - tableType.key = tableTypeKey; - tableType.type = tableTypeType; - tableType.value = tableTypeValue; - displayName = new OdkTablesKeyValueStoreEntry(); displayName.tableId = T.tableId; displayName.partition = tablePartition; @@ -98,14 +80,6 @@ public static class OdkTablesKeyValueStoreEntries { displayName.type = displayNameType; displayName.value = displayNameValue; - tableAccessControls = new OdkTablesKeyValueStoreEntry(); - tableAccessControls.tableId = T.tableId; - tableAccessControls.partition = tablePartition; - tableAccessControls.aspect = tableAspect; - tableAccessControls.key = tableAccessControlTableIdKey; - tableAccessControls.type = tableAccessControlTableIdType; - tableAccessControls.value = tableAccessControlTableIdValue; - } } @@ -197,9 +171,7 @@ public String getWeight() { static { kvsEntries.add(T.OdkTablesKeyValueStoreEntries.entryOne); kvsEntries.add(T.OdkTablesKeyValueStoreEntries.entryTwo); - kvsEntries.add(T.OdkTablesKeyValueStoreEntries.tableType); kvsEntries.add(T.OdkTablesKeyValueStoreEntries.displayName); - kvsEntries.add(T.OdkTablesKeyValueStoreEntries.tableAccessControls); columns.add(T.Columns.column_name); columns.add(T.Columns.column_age); columns.add(T.Columns.column_weight); From da616225eb1f30072da772ebf275b36263e4fd20 Mon Sep 17 00:00:00 2001 From: mitchellsundt Date: Fri, 25 Apr 2014 14:57:50 -0700 Subject: [PATCH 06/13] Add filterScope and savepointType to protocol. Change URL paths for ODK Tables sync. Split app/table file sync from instance file sync. Remove unused servlets. --- eclipse-aggregate-gae/war/WEB-INF/web.xml | 20 -- .../client/odktables/FileSummaryClient.java | 8 +- .../table/OdkTablesViewTableFileInfo.java | 122 +-------- .../aggregate/odktables/DataManager.java | 7 +- .../odktables/FileManifestManager.java | 26 +- .../odktables/api/FileManifestService.java | 31 +-- .../aggregate/odktables/api/FileService.java | 7 +- .../odktables/api/InstanceFileService.java | 83 ++++++ .../aggregate/odktables/api/TableService.java | 30 ++- .../OdkTablesKeyValueManifestManager.java | 111 -------- .../impl/api/FileManifestServiceImpl.java | 47 ++-- .../odktables/impl/api/FileServiceImpl.java | 102 ++++---- .../impl/api/InstanceFileServiceImpl.java | 236 ++++++++++++++++++ .../odktables/impl/api/TableServiceImpl.java | 7 + .../odktables/relation/DbLogTable.java | 31 ++- .../aggregate/odktables/relation/DbTable.java | 33 ++- .../odktables/relation/DbTableFileInfo.java | 63 ++++- .../odktables/relation/DbTableFiles.java | 46 ---- .../relation/DbTableInstanceFiles.java | 41 +++ .../odktables/relation/EntityConverter.java | 5 +- .../odktables/relation/EntityCreator.java | 20 +- .../odktables/rest/ConflictType.java | 64 +++++ .../rest/SavepointTypeManipulator.java | 61 +++++ .../aggregate/odktables/rest/SyncState.java | 50 ++++ .../odktables/rest/TableConstants.java | 77 ++++-- .../aggregate/odktables/rest/entity/Row.java | 49 +++- .../odktables/rest/entity/Scope.java | 18 ++ .../TextPlainHttpMessageConverter.java | 22 ++ .../server/ServerDataServiceImpl.java | 9 +- .../aggregate/server/ServerOdkTablesUtil.java | 4 +- .../OdkTablesFileUploadFromPhoneServlet.java | 17 -- .../servlet/OdkTablesManifestServlet.java | 117 --------- .../OdkTablesTableFileDownloadServlet.java | 141 ----------- .../aggregate/odktables/AuthFilterTest.java | 17 +- .../aggregate/odktables/DataManagerTest.java | 25 +- .../opendatakit/aggregate/odktables/T.java | 9 +- .../odktables/entity/SerializationTest.java | 16 +- 37 files changed, 985 insertions(+), 787 deletions(-) create mode 100644 src/main/java/org/opendatakit/aggregate/odktables/api/InstanceFileService.java create mode 100644 src/main/java/org/opendatakit/aggregate/odktables/impl/api/InstanceFileServiceImpl.java create mode 100644 src/main/java/org/opendatakit/aggregate/odktables/relation/DbTableInstanceFiles.java create mode 100644 src/main/java/org/opendatakit/aggregate/odktables/rest/ConflictType.java create mode 100644 src/main/java/org/opendatakit/aggregate/odktables/rest/SavepointTypeManipulator.java create mode 100644 src/main/java/org/opendatakit/aggregate/odktables/rest/SyncState.java create mode 100644 src/main/java/org/opendatakit/aggregate/odktables/rest/serialization/TextPlainHttpMessageConverter.java delete mode 100644 src/main/java/org/opendatakit/aggregate/servlet/OdkTablesFileUploadFromPhoneServlet.java delete mode 100644 src/main/java/org/opendatakit/aggregate/servlet/OdkTablesManifestServlet.java delete mode 100644 src/main/java/org/opendatakit/aggregate/servlet/OdkTablesTableFileDownloadServlet.java diff --git a/eclipse-aggregate-gae/war/WEB-INF/web.xml b/eclipse-aggregate-gae/war/WEB-INF/web.xml index 0f5d82cfab..7c379b4e74 100644 --- a/eclipse-aggregate-gae/war/WEB-INF/web.xml +++ b/eclipse-aggregate-gae/war/WEB-INF/web.xml @@ -277,26 +277,6 @@ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ /tableFileUpload - - - tableFileDownload - org.opendatakit.aggregate.servlet.OdkTablesTableFileDownloadServlet - - - tableFileDownload - /tableFileDownload - - - - - tableKeyValueManifest - org.opendatakit.aggregate.servlet.OdkTablesManifestServlet - - - tableKeyValueManifest - /tableKeyValueManifest - - uploadTableFromCSV diff --git a/src/main/java/org/opendatakit/aggregate/client/odktables/FileSummaryClient.java b/src/main/java/org/opendatakit/aggregate/client/odktables/FileSummaryClient.java index d4bf1406ed..c2e1cf9a3b 100644 --- a/src/main/java/org/opendatakit/aggregate/client/odktables/FileSummaryClient.java +++ b/src/main/java/org/opendatakit/aggregate/client/odktables/FileSummaryClient.java @@ -41,6 +41,7 @@ public class FileSummaryClient implements Serializable { // need to add and ID and change mediaFiles to numMediaFiles. // also should probably add the table name and table id. private String id; + private String odkClientVersion; private String tableId; private String downloadUrl; @@ -50,11 +51,12 @@ private FileSummaryClient() { } public FileSummaryClient(String filename, String contentType, Long contentLength, - String id, String tableId, String downloadUrl) { + String id, String odkClientVersion, String tableId, String downloadUrl) { this.filename = filename; this.contentType = contentType; this.contentLength = contentLength; this.id = id; + this.odkClientVersion = odkClientVersion; this.tableId = tableId; this.downloadUrl = downloadUrl; } @@ -75,6 +77,10 @@ public String getId() { return id; } + public String getOdkClientVersion() { + return odkClientVersion; + } + public String getTableId() { return tableId; } diff --git a/src/main/java/org/opendatakit/aggregate/client/table/OdkTablesViewTableFileInfo.java b/src/main/java/org/opendatakit/aggregate/client/table/OdkTablesViewTableFileInfo.java index 8d340a7652..6f61a7bca0 100644 --- a/src/main/java/org/opendatakit/aggregate/client/table/OdkTablesViewTableFileInfo.java +++ b/src/main/java/org/opendatakit/aggregate/client/table/OdkTablesViewTableFileInfo.java @@ -26,12 +26,9 @@ import org.opendatakit.aggregate.client.odktables.FileSummaryClient; import org.opendatakit.aggregate.client.odktables.TableContentsForFilesClient; import org.opendatakit.aggregate.client.odktables.TableEntryClient; -import org.opendatakit.aggregate.client.popups.OdkTablesMediaFileListPopup; import org.opendatakit.aggregate.client.widgets.OdkTablesDeleteFileButton; import org.opendatakit.aggregate.constants.common.SubTabs; -import com.google.gwt.event.dom.client.ClickEvent; -import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.Anchor; import com.google.gwt.user.client.ui.FlexTable; @@ -51,35 +48,28 @@ public class OdkTablesViewTableFileInfo extends FlexTable { private TableEntryClient currentTable; // that table's info rows - // private List rows; - private ArrayList fileSummaries; - // columnnames. - private ArrayList columnNames; - private static final int DELETE_COLUMN = 0; // this is the heading for the delete row button. private static final String DELETE_HEADING = "Delete"; - private static final int FILENAME_COLUMN = 2; - private static final String FILENAME_HEADING = "Filename"; private static final int KEY_COLUMN = 1; private static final String KEY_HEADING = "Key"; - private static final int DOWNLOAD_COLUMN = 3; + private static final int ODK_CLIENT_VERSION_COLUMN = 2; + private static final String ODK_CLIENT_VERSION_HEADING = "Client Version"; + private static final int TABLE_ID_COLUMN = 3; + private static final String TABLE_ID_HEADING = "TableId"; + private static final int FILENAME_COLUMN = 4; + private static final String FILENAME_HEADING = "Filename"; + private static final int DOWNLOAD_COLUMN = 5; private static final String DOWNLOAD_HEADING = "Download"; - private static final int numColumns = 4; + private static final int numColumns = 6; // this is just the tab that opened the table private AggregateSubTabBase basePanel; - // this is the number of columns that exist for a table as returned - // by the server that are NOT user defined. - private static final int NUMBER_ADMIN_COLUMNS = 2; - - // the message to display when there is no data in the table. - private static String NO_DATA_MESSAGE = "There is no data in this table."; // the message to display when there are no rows in the table private static String NO_ROWS_MESSAGE = "There are no rows to display."; @@ -163,51 +153,14 @@ public void onSuccess(TableContentsForFilesClient tcc) { private void setColumnHeadings() { // create the table headers. setText(0, DELETE_COLUMN, DELETE_HEADING); - setText(0, FILENAME_COLUMN, FILENAME_HEADING); setText(0, KEY_COLUMN, KEY_HEADING); + setText(0, ODK_CLIENT_VERSION_COLUMN, ODK_CLIENT_VERSION_HEADING); + setText(0, TABLE_ID_COLUMN, TABLE_ID_HEADING); + setText(0, FILENAME_COLUMN, FILENAME_HEADING); setText(0, DOWNLOAD_COLUMN, DOWNLOAD_HEADING); getRowFormatter().addStyleName(0, "titleBar"); } - /* - * This is the method that actually updates the column headings. It is its own - * method so that it can be called cleanly in the updateTableData method. If - * the code is AFTER the call to SecureGWT, as it was at first, you can get - * null pointer exceptions, as the async callback may have not returned. - */ - // private void setColumnHeadings(List columns) { - // this.removeAllRows(); - // - // //Window.alert(Integer.toString(columns.size())); - // - // // If there are no user-defined columns display the message. - // // Otherwise set the headings. - // if (columns.size() == NUMBER_ADMIN_COLUMNS) { - // setText(0, 0, NO_DATA_MESSAGE); - // } else { - // // set the delete column - // setText(0, DELETE_ROW_COLUMN, DELETE_ROW_HEADING); - // setText(0, MEDIA_FILE_COLUMN, MEDIA_FILE_HEADING); - // // make the headings - // int i = 2; - // for (String name : this.columnNames) { - // // TODO work on ordering the names properly - // // - // // the rows that come through beginning with "_" are user-defined. - // // we only want to display those (as long as you're not displaying - // // metadata), so select those, remove them, and add them. - // if (name.substring(0, 1).equalsIgnoreCase("_")) { - // setText(0, i, name.substring(1, name.length())); - // i++; - // } - // } - // - // - // getRowFormatter().addStyleName(0, "titleBar"); - // } - // - // } - /* * This will set the row values in the listbox. */ @@ -232,6 +185,8 @@ private void setRows() { setWidget(currentRow, DELETE_COLUMN, new OdkTablesDeleteFileButton(this.basePanel, currentTable.getTableId(), sum.getId())); setText(currentRow, KEY_COLUMN, sum.getId()); + setText(currentRow, ODK_CLIENT_VERSION_COLUMN, sum.getOdkClientVersion()); + setText(currentRow, TABLE_ID_COLUMN, sum.getTableId()); setText(currentRow, FILENAME_COLUMN, sum.getFilename()); Widget downloadCol; if (sum.getDownloadUrl() != null) { @@ -252,57 +207,6 @@ private void setRows() { removeRow(getRowCount() - 1); } } - - // for (FileSummaryClient summary : fileSummaries) { - // // the rows that come through beginning with "_" are user-defined. - // // we only want to display those (as long as you're not displaying - // // metadata), so select those, remove them, and add them. - // // i is counter, j is columns. fill row by moving to the right, then - // // move on - // // currentRow is the actual current row. this is different than the - // // counter, b/c - // // you can also get deleted rows returned. - // - // // don't display deleted rows (although the dm doesn't return them at - // // this point) - // if (!row.isDeleted()) { - // // now set the delete button - // setWidget(currentRow, 0, - // new OdkTablesDeleteFileButton(this.basePanel, - // currentTable.getTableId(), row.getRowId())); - // int j = 1; - // for (String column : columnNames) { - // if (column.substring(0, 1).equalsIgnoreCase("_")) { - // setWidget(currentRow, j, new HTML(row.getValues().get(column))); - // j++; - // } - // - // if (currentRow % 2 == 0) { - // getRowFormatter().addStyleName(currentRow, "evenTableRow"); - // } - // } - // } - // currentRow++; - // } - // } - } - - private class MediaFileListClickHandler implements ClickHandler { - - private String tableId; - private String key; - - public MediaFileListClickHandler(String tableId, String key) { - this.tableId = tableId; - this.key = key; - } - - @Override - public void onClick(ClickEvent event) { - OdkTablesMediaFileListPopup mediaListpopup = new OdkTablesMediaFileListPopup(tableId, key); - mediaListpopup.setPopupPositionAndShow(mediaListpopup.getPositionCallBack()); - } - } } diff --git a/src/main/java/org/opendatakit/aggregate/odktables/DataManager.java b/src/main/java/org/opendatakit/aggregate/odktables/DataManager.java index ea8284a294..778285825f 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/DataManager.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/DataManager.java @@ -146,10 +146,11 @@ private void revertPendingChanges(DbTableEntryEntity entry, List entities = DbTableFileInfo.queryForApp(cc); - ArrayList manifestEntries = getEntriesFromQuery(entities, dbTableFiles); - OdkTablesFileManifest manifest = new OdkTablesFileManifest(manifestEntries); - return manifest; - } - /** * Get the manifest entries for the files associated with the given table. * @@ -85,7 +72,7 @@ public OdkTablesFileManifest getManifestForAllAppFiles() throws ODKDatastoreExce public OdkTablesFileManifest getManifestForTable(String tableId) throws ODKDatastoreException { // TODO: need to handle access control. DbTableFiles dbTableFiles = new DbTableFiles(cc); - List entities = DbTableFileInfo.queryForTableId(tableId, cc); + List entities = DbTableFileInfo.queryForTableIdFiles(odkClientVersion, tableId, cc); ArrayList manifestEntries = getEntriesFromQuery(entities, dbTableFiles); OdkTablesFileManifest manifest = new OdkTablesFileManifest(manifestEntries); return manifest; @@ -102,8 +89,7 @@ public OdkTablesFileManifest getManifestForTable(String tableId) throws ODKDatas public OdkTablesFileManifest getManifestForAppLevelFiles() throws ODKDatastoreException { // TODO: need to handle access control. DbTableFiles dbTableFiles = new DbTableFiles(cc); - List entities = DbTableFileInfo.queryForTableId( - FileServiceImpl.NO_TABLE_ID, cc); + List entities = DbTableFileInfo.queryForAppLevelFiles(odkClientVersion, cc); ArrayList manifestEntries = getEntriesFromQuery(entities, dbTableFiles); OdkTablesFileManifest manifest = new OdkTablesFileManifest(manifestEntries); return manifest; @@ -149,7 +135,7 @@ private ArrayList getEntriesFromQuery( entry.md5hash = blobEntitySet.getContentHash(1, cc); String urlPartial = cc.getServerURL() + BasicConsts.FORWARDSLASH + ServletConsts.ODK_TABLES_SERVLET_BASE_PATH + BasicConsts.FORWARDSLASH - + FileService.SERVLET_PATH + BasicConsts.FORWARDSLASH + appId + BasicConsts.FORWARDSLASH + + appId + BasicConsts.FORWARDSLASH + FileService.SERVLET_PATH + BasicConsts.FORWARDSLASH + pathToFile; String urlComplete = HtmlUtil.createLinkWithProperties(urlPartial, properties); entry.downloadUrl = urlComplete; diff --git a/src/main/java/org/opendatakit/aggregate/odktables/api/FileManifestService.java b/src/main/java/org/opendatakit/aggregate/odktables/api/FileManifestService.java index b52b3a1899..f04367d89f 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/api/FileManifestService.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/api/FileManifestService.java @@ -19,7 +19,6 @@ import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; @@ -34,20 +33,24 @@ * @author sudar.sam@gmail.com * */ -@Path("filemanifest") +@Path("{appId}/manifest/{odkClientVersion}") @Produces({MediaType.APPLICATION_JSON, ApiConstants.MEDIA_TEXT_XML_UTF8, ApiConstants.MEDIA_APPLICATION_XML_UTF8}) public interface FileManifestService { /** - * URL parameter specifying the tableId. Optional. If not present, will return - * all the files for the application. - */ - public static final String PARAM_TABLE_ID = "table_id"; - /** - * URL parameter specifying whether or not only the app level files should be - * returned. If present, this will override the tableId parameter. + * + * @param servletContext + * @param req + * @param resp + * @param appId + * @param tableId + * @param appLevel + * @return {@link OdkTablesFileManifest} of all the files meeting the filter criteria. */ - public static final String PARAM_APP_LEVEL_FILES = "app_level_files"; + @GET + @Path("") + @GZIP + public Response /*OdkTablesFileManifest*/ getAppLevelFileManifest(@PathParam("appId") String appId, @PathParam("odkClientVersion") String odkClientVersion); /** * @@ -60,10 +63,8 @@ public interface FileManifestService { * @return {@link OdkTablesFileManifest} of all the files meeting the filter criteria. */ @GET - @Path("{appId}") + @Path("{tableId}") @GZIP - public Response /*OdkTablesFileManifest*/ getFileManifest( - @PathParam("appId") String appId, @QueryParam(PARAM_TABLE_ID) String tableId, - @QueryParam(PARAM_APP_LEVEL_FILES) String appLevel); - + public Response /*OdkTablesFileManifest*/ getTableIdFileManifest( + @PathParam("appId") String appId, @PathParam("odkClientVersion") String odkClientVersion, @PathParam("tableId") String tableId); } diff --git a/src/main/java/org/opendatakit/aggregate/odktables/api/FileService.java b/src/main/java/org/opendatakit/aggregate/odktables/api/FileService.java index 815f354587..0c1058fef9 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/api/FileService.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/api/FileService.java @@ -50,7 +50,7 @@ * @author sudar.sam@gmail.com * */ -@Path("/files") +@Path("{appId}/files/{odkClientVersion}") public interface FileService { /** @@ -62,16 +62,17 @@ public interface FileService { public static final String PARAM_AS_ATTACHMENT = "as_attachment"; public static final String ERROR_MSG_INSUFFICIENT_PATH = "Not Enough Path Segments: must be at least 2."; public static final String ERROR_MSG_UNRECOGNIZED_APP_ID = "Unrecognized app id: "; + public static final String ERROR_MSG_PATH_NOT_UNDER_APP_ID = "File path is not under app id: "; public static final String MIME_TYPE_IMAGE_JPEG = "image/jpeg"; @GET @Path("{filePath:.*}") @GZIP - public Response getFile(@PathParam("filePath") List segments, @QueryParam(PARAM_AS_ATTACHMENT) String asAttachment) throws IOException; + public Response getFile(@PathParam("appId") String appId, @PathParam("odkClientVersion") String odkClientVersion, @PathParam("filePath") List segments, @QueryParam(PARAM_AS_ATTACHMENT) String asAttachment) throws IOException; @POST @Path("{filePath:.*}") @Consumes({MediaType.MEDIA_TYPE_WILDCARD}) - public Response putFile(@Context HttpServletRequest req, @PathParam("filePath") List segments, @GZIP byte[] content) throws IOException, ODKTaskLockException; + public Response putFile(@Context HttpServletRequest req, @PathParam("appId") String appId, @PathParam("odkClientVersion") String odkClientVersion, @PathParam("filePath") List segments, @GZIP byte[] content) throws IOException, ODKTaskLockException; } diff --git a/src/main/java/org/opendatakit/aggregate/odktables/api/InstanceFileService.java b/src/main/java/org/opendatakit/aggregate/odktables/api/InstanceFileService.java new file mode 100644 index 0000000000..cd8ffe6799 --- /dev/null +++ b/src/main/java/org/opendatakit/aggregate/odktables/api/InstanceFileService.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2012-2013 University of Washington + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.opendatakit.aggregate.odktables.api; + +import java.io.IOException; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.PathSegment; +import javax.ws.rs.core.Response; + +import org.jboss.resteasy.annotations.GZIP; +import org.opendatakit.common.persistence.exception.ODKTaskLockException; + +/** + * Servlet for handling the uploading and downloading of instance data files + * (instance attachments) from the phone. + *

+ * The general idea is that the interaction with the actual files will occur at + * /odktables/{appId}/tables/{tableId}/instances/{instanceId}/subpathToFile. + *

+ * The interface only supports puts and gets -- no directory listings. + *

+ * Files will thus be referred to by their unrooted path relative to the + * /sdcard/opendatakit/{appId}/ directory on the device. + *

+ * A GET request to that url will download the file. A POST request to that url + * must contain an entity that is the file, as well as a table id parameter on + * the POST itself. + *

+ * These urls should be generated by a file manifest servlet on a table id + * basis. + * + * @author sudar.sam@gmail.com + * + */ +public interface InstanceFileService { + + /** + * The url of the servlet that for downloading and uploading files. This must + * be appended to the odk table service. + */ + public static final String SERVLET_PATH = "files"; + + public static final String PARAM_AS_ATTACHMENT = "as_attachment"; + public static final String PARAM_MD5_HASH_ONLY = "md5_hash_only"; + public static final String ERROR_MSG_INSUFFICIENT_PATH = "Not Enough Path Segments: must be at least 2."; + public static final String ERROR_MSG_UNRECOGNIZED_APP_ID = "Unrecognized app id: "; + public static final String ERROR_MSG_PATH_NOT_UNDER_APP_ID = "File path is not under app id: "; + public static final String MIME_TYPE_IMAGE_JPEG = "image/jpeg"; + + @GET + @Path("{filePath:.*}") + @GZIP + public Response getFile(@PathParam("filePath") List segments, @QueryParam(PARAM_AS_ATTACHMENT) String asAttachment, @QueryParam(PARAM_MD5_HASH_ONLY) String asMd5HashOnly) throws IOException; + + @POST + @Path("{filePath:.*}") + @Consumes({MediaType.MEDIA_TYPE_WILDCARD}) + public Response putFile(@Context HttpServletRequest req, @PathParam("filePath") List segments, @GZIP byte[] content) throws IOException, ODKTaskLockException; + +} diff --git a/src/main/java/org/opendatakit/aggregate/odktables/api/TableService.java b/src/main/java/org/opendatakit/aggregate/odktables/api/TableService.java index 86480f5a88..932c987a19 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/api/TableService.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/api/TableService.java @@ -37,7 +37,7 @@ import org.opendatakit.common.persistence.exception.ODKDatastoreException; import org.opendatakit.common.persistence.exception.ODKTaskLockException; -@Path("/tables") +@Path("{appId}/tables") @Produces({MediaType.APPLICATION_JSON, ApiConstants.MEDIA_TEXT_XML_UTF8, ApiConstants.MEDIA_APPLICATION_XML_UTF8}) public interface TableService { @@ -49,7 +49,7 @@ public interface TableService { * @throws ODKDatastoreException */ @GET - @Path("{appId}") + @Path("") @GZIP public Response /*TableResourceList*/ getTables(@PathParam("appId") String appId) throws ODKDatastoreException; @@ -62,7 +62,7 @@ public interface TableService { * @throws PermissionDeniedException */ @GET - @Path("{appId}/{tableId}") + @Path("{tableId}") @GZIP public Response /*TableResource*/ getTable(@PathParam("appId") String appId, @PathParam("tableId") String tableId) throws ODKDatastoreException, PermissionDeniedException; @@ -79,7 +79,7 @@ public interface TableService { * @throws ODKTaskLockException */ @PUT - @Path("{appId}/{tableId}") + @Path("{tableId}") @Consumes({MediaType.APPLICATION_JSON, ApiConstants.MEDIA_TEXT_XML_UTF8, ApiConstants.MEDIA_APPLICATION_XML_UTF8}) @GZIP public Response /*TableResource*/ createTable(@PathParam("appId") String appId, @PathParam("tableId") String tableId, @GZIP TableDefinition definition) @@ -95,7 +95,7 @@ public interface TableService { * @throws PermissionDeniedException */ @DELETE - @Path("{appId}/{tableId}") + @Path("{tableId}") public Response /*void*/ deleteTable(@PathParam("appId") String appId, @PathParam("tableId") String tableId) throws ODKDatastoreException, ODKTaskLockException, PermissionDeniedException; @@ -109,7 +109,7 @@ public interface TableService { * @throws ODKTaskLockException */ @GET - @Path("{appId}/{tableId}/definition") + @Path("{tableId}/definition") @GZIP public Response /*TableDefinitionResource*/ getDefinition(@PathParam("appId") String appId, @PathParam("tableId") String tableId) throws ODKDatastoreException, PermissionDeniedException, ODKTaskLockException; @@ -121,7 +121,7 @@ public interface TableService { * @return {@link PropertiesService} for accessing table metadata * @throws ODKDatastoreException */ - @Path("{appId}/{tableId}/properties") + @Path("{tableId}/properties") public PropertiesService getProperties(@PathParam("appId") String appId, @PathParam("tableId") String tableId) throws ODKDatastoreException; @@ -132,7 +132,7 @@ public PropertiesService getProperties(@PathParam("appId") String appId, @PathPa * @return {@link DataService} for manipulating row data in this table. * @throws ODKDatastoreException */ - @Path("{appId}/{tableId}/rows") + @Path("{tableId}/rows") public DataService getData(@PathParam("appId") String appId, @PathParam("tableId") String tableId) throws ODKDatastoreException; /** @@ -142,7 +142,7 @@ public PropertiesService getProperties(@PathParam("appId") String appId, @PathPa * @return {@link DiffService} for the row-changes on this table. * @throws ODKDatastoreException */ - @Path("{appId}/{tableId}/diff") + @Path("{tableId}/diff") public DiffService getDiff(@PathParam("appId") String appId, @PathParam("tableId") String tableId) throws ODKDatastoreException; /** @@ -152,6 +152,16 @@ public PropertiesService getProperties(@PathParam("appId") String appId, @PathPa * @return {@link TableAclService} for ACL management on this table. * @throws ODKDatastoreException */ - @Path("{appId}/{tableId}/acl") + @Path("{tableId}/acl") public TableAclService getAcl(@PathParam("appId") String appId, @PathParam("tableId") String tableId) throws ODKDatastoreException; + + /** + * + * @param appId + * @param tableId + * @return {@link TableAclService} for ACL management on this table. + * @throws ODKDatastoreException + */ + @Path("{tableId}/instances") + public InstanceFileService getInstanceFiles(@PathParam("appId") String appId, @PathParam("tableId") String tableId) throws ODKDatastoreException; } diff --git a/src/main/java/org/opendatakit/aggregate/odktables/entity/serialization/OdkTablesKeyValueManifestManager.java b/src/main/java/org/opendatakit/aggregate/odktables/entity/serialization/OdkTablesKeyValueManifestManager.java index 898bf7c6d5..752269b4cc 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/entity/serialization/OdkTablesKeyValueManifestManager.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/entity/serialization/OdkTablesKeyValueManifestManager.java @@ -18,32 +18,14 @@ import java.io.IOException; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import org.codehaus.jackson.JsonGenerationException; import org.codehaus.jackson.map.JsonMappingException; import org.codehaus.jackson.map.ObjectMapper; -import org.opendatakit.aggregate.client.exception.RequestFailureException; -import org.opendatakit.aggregate.constants.ServletConsts; -import org.opendatakit.aggregate.odktables.TableManager; -import org.opendatakit.aggregate.odktables.exception.PermissionDeniedException; -import org.opendatakit.aggregate.odktables.relation.DbTableFileInfo; -import org.opendatakit.aggregate.odktables.relation.DbTableFiles; -import org.opendatakit.aggregate.odktables.relation.EntityConverter; -import org.opendatakit.aggregate.odktables.rest.entity.OdkTablesFileManifestEntry; import org.opendatakit.aggregate.odktables.rest.entity.OdkTablesKeyValueStoreEntry; -import org.opendatakit.aggregate.odktables.rest.entity.Row; -import org.opendatakit.aggregate.odktables.rest.entity.TableEntry; import org.opendatakit.aggregate.odktables.security.TablesUserPermissions; -import org.opendatakit.aggregate.servlet.OdkTablesTableFileDownloadServlet; -import org.opendatakit.common.persistence.client.exception.DatastoreFailureException; -import org.opendatakit.common.persistence.exception.ODKDatastoreException; -import org.opendatakit.common.security.client.exception.AccessDeniedException; -import org.opendatakit.common.utils.HtmlUtil; import org.opendatakit.common.web.CallingContext; -import org.opendatakit.common.web.constants.BasicConsts; /** * This class manages the creation of the entries in the manifest. It creates @@ -81,99 +63,6 @@ public OdkTablesKeyValueManifestManager(String appId, String tableId, TablesUser entries = new ArrayList(); } - /** - * Get the manifest in the format of a JSON String. It generates the manifest - * the first time it is called. This meant if the object was created and - * something was changed in the datastore, it might not be up to date. This - * seems unlikely/ unimportant. - * - * @return - * @throws IOException - * @throws JsonMappingException - * @throws JsonGenerationException - * @throws AccessDeniedException - * @throws RequestFailureException - * @throws DatastoreFailureException - * @throws PermissionDeniedException - */ - public String getManifest() throws JsonGenerationException, JsonMappingException, IOException, - DatastoreFailureException, RequestFailureException, - AccessDeniedException, PermissionDeniedException { - if (manifest == null) { - entries = getEntries(); - manifest = mapper.writeValueAsString(entries); - } - return manifest; - } - - /** - * Gets the entries in the manifest for the tableId. - * @throws PermissionDeniedException - */ - public List getEntries() throws - DatastoreFailureException, RequestFailureException, AccessDeniedException, - JsonGenerationException, IOException, PermissionDeniedException { - - try { - List infoRows = EntityConverter.toRowsFromFileInfo(DbTableFileInfo.queryForTableId( - tableId, cc)); - TableManager tm = new TableManager(appId, userPermissions, cc); - TableEntry table = tm.getTable(tableId); - DbTableFiles blobSetRelation = new DbTableFiles(cc); - List entries = new ArrayList(); - for (Row row : infoRows) { - // we only want the non-deleted rows - if (!row.isDeleted()) { - // the KeyValueStoreEntry object is the same for every entry. However, - // for files you need to create a FileManifestEntry for the value. - OdkTablesKeyValueStoreEntry entry = new OdkTablesKeyValueStoreEntry(); - entry.tableId = tableId; - // TODO: all these things got commented out when we redid the file - // stuff. needs - // to be re-implemented. - entry.key = "OdkTablesKeyValueManifestManager not implemented"; - entry.type = "OdkTablesKeyValueManifestManager not implemented"; - // if it's a file, make the file manifest entry. - // if (entry.type.equalsIgnoreCase(DbTableFileInfo.Type.FILE.name)) { - if (entry.type.equalsIgnoreCase("unimplemented")) { - OdkTablesFileManifestEntry fileEntry = new OdkTablesFileManifestEntry(); - // fileEntry.filename = - // blobSetRelation.getBlobEntitySet(row.getValues().get(DbTableFileInfo.VALUE), - // cc) - // .getUnrootedFilename(1, cc); - // fileEntry.md5hash = - // blobSetRelation.getBlobEntitySet(row.getValues().get(DbTableFileInfo.VALUE), - // cc) - // .getContentHash(1, cc); - // now generate the download url. look at XFormsManifestXmlTable as - // an - // example of how Mitch did it. - Map properties = new HashMap(); - // properties.put(ServletConsts.BLOB_KEY, - // row.getValues().get(DbTableFileInfo.VALUE)); - properties.put(ServletConsts.AS_ATTACHMENT, "true"); - String url = cc.getServerURL() + BasicConsts.FORWARDSLASH - + OdkTablesTableFileDownloadServlet.ADDR; - fileEntry.downloadUrl = HtmlUtil.createLinkWithProperties(url, properties); - // now convert this object to json and set it to the entry's value. - ObjectMapper mapper = new ObjectMapper(); - entry.value = mapper.writeValueAsString(fileEntry); - - } else { - // if it's not a file, we just set the value. as input. - // entry.value = row.getValues().get(DbTableFileInfo.VALUE); - } - // and now add the completed entry to the list of entries - entries.add(entry); - - } - } - return entries; - } catch (ODKDatastoreException e) { - e.printStackTrace(); - throw new DatastoreFailureException(e); - } - } /** * A single add method for testing json serialization. diff --git a/src/main/java/org/opendatakit/aggregate/odktables/impl/api/FileManifestServiceImpl.java b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/FileManifestServiceImpl.java index 64aa0e2ae9..27713c9654 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/impl/api/FileManifestServiceImpl.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/FileManifestServiceImpl.java @@ -19,7 +19,6 @@ import javax.servlet.http.HttpServletRequest; import javax.ws.rs.GET; import javax.ws.rs.PathParam; -import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.Response; @@ -63,28 +62,42 @@ public FileManifestServiceImpl(@Context ServletContext sc, @Context HttpServletR @Override @GET @GZIP - public Response getFileManifest(@PathParam("appId") String appId, @QueryParam(PARAM_TABLE_ID) String tableId, - @QueryParam(PARAM_APP_LEVEL_FILES) String appLevel) { + public Response getAppLevelFileManifest(@PathParam("appId") String appId, @PathParam("odkClientVersion") String odkClientVersion) { // Now make sure we have an app id. if (appId == null || "".equals(appId)) { return Response.status(Status.BAD_REQUEST).entity("Invalid request. App id must be present and valid.").build(); } - FileManifestManager manifestManager = new FileManifestManager(appId, cc); + FileManifestManager manifestManager = new FileManifestManager(appId, odkClientVersion, cc); OdkTablesFileManifest manifest = null; try { - // Now we need to decide what level on this app we're going to use. We - // can do table-level, all files, or app-level. The app-level param - // trumps the table-level. - if (appLevel != null && !"".equals(appLevel)) { - // we want just the app-level files. - manifest = manifestManager.getManifestForAppLevelFiles(); - } else if (tableId != null && !"".equals(tableId)) { - // we want just the files for the table. - manifest = manifestManager.getManifestForTable(tableId); - } else { - // we want them all! - manifest = manifestManager.getManifestForAllAppFiles(); - } + // we want just the app-level files. + manifest = manifestManager.getManifestForAppLevelFiles(); + } catch (ODKDatastoreException e) { + Log log = LogFactory.getLog(FileManifestServiceImpl.class); + log.error("Datastore exception in getting the file manifest"); + e.printStackTrace(); + } + if (manifest == null) { + return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Unable to retrieve manifest.").build(); + } else { + return Response.ok(manifest).build(); + } + } + + + @Override + @GET + @GZIP + public Response getTableIdFileManifest(@PathParam("appId") String appId, @PathParam("odkClientVersion") String odkClientVersion, @PathParam("tableId") String tableId) { + // Now make sure we have an app id. + if (appId == null || "".equals(appId)) { + return Response.status(Status.BAD_REQUEST).entity("Invalid request. App id must be present and valid.").build(); + } + FileManifestManager manifestManager = new FileManifestManager(appId, odkClientVersion, cc); + OdkTablesFileManifest manifest = null; + try { + // we want just the files for the table. + manifest = manifestManager.getManifestForTable(tableId); } catch (ODKDatastoreException e) { Log log = LogFactory.getLog(FileManifestServiceImpl.class); log.error("Datastore exception in getting the file manifest"); diff --git a/src/main/java/org/opendatakit/aggregate/odktables/impl/api/FileServiceImpl.java b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/FileServiceImpl.java index 3edcf95c43..7b3ed880dd 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/impl/api/FileServiceImpl.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/FileServiceImpl.java @@ -17,6 +17,7 @@ import java.io.IOException; import java.util.List; +import java.util.Locale; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; @@ -49,6 +50,7 @@ import org.opendatakit.aggregate.odktables.relation.EntityCreator; import org.opendatakit.aggregate.odktables.security.TablesUserPermissions; import org.opendatakit.aggregate.odktables.security.TablesUserPermissionsImpl; +import org.opendatakit.common.datamodel.BinaryContentManipulator.BlobSubmissionOutcome; import org.opendatakit.common.ermodel.BlobEntitySet; import org.opendatakit.common.persistence.exception.ODKDatastoreException; import org.opendatakit.common.persistence.exception.ODKTaskLockException; @@ -60,13 +62,6 @@ public class FileServiceImpl implements FileService { private static final Log LOGGER = LogFactory.getLog(FileServiceImpl.class); - /** - * String to stand in for those things in the app's root directory. - * - * NOTE: This cannot be null -- GAE doesn't like that! - */ - public static final String NO_TABLE_ID = ""; - private static final String PATH_DELIMITER = "/"; /** @@ -76,6 +71,8 @@ public class FileServiceImpl implements FileService { * @see #getTableIdFromPathSegments(List) */ private static final String TABLES_FOLDER = "tables"; + private static final String ASSETS_FOLDER = "assets"; + private static final String CSV_FOLDER = "csv"; private CallingContext cc; private TablesUserPermissions userPermissions; private UriInfo info; @@ -92,27 +89,24 @@ public FileServiceImpl(@Context ServletContext sc, @Context HttpServletRequest r @GET @Path("{filePath:.*}") // because we want to get the whole path - public Response getFile(@PathParam("filePath") List segments, @QueryParam(PARAM_AS_ATTACHMENT) String asAttachment) throws IOException { - // First we need to get the app id and the table id from the path. We're - // going to be assuming that you're passing the entire path of the file you - // want to get. By convention, this is: appid/tableid/the/rest/of/path. + public Response getFile(@PathParam("appId") String appId, @PathParam("odkClientVersion") String odkClientVersion, @PathParam("filePath") List segments, @QueryParam(PARAM_AS_ATTACHMENT) String asAttachment) throws IOException { + // First we need to get the table id from the path. We're + // going to be assuming that you're passing the entire path of the file + // under /sdcard/opendatakit/appId/ e.g., tables/tableid/the/rest/of/path. // So we'll reclaim the tidbits and then reconstruct the entire path. - // Note that even if you're getting general files, like perhaps the jquery - // library, you will be sure to have an appid and the table name, so these - // calls should never fail. Try to enforce this, however. - if (segments.size() <= 1) { + // If you are getting general files, there will be no recoverable tableId, + // and these are then app-level files. + if (segments.size() < 1) { return Response.status(Status.BAD_REQUEST).entity(FileService.ERROR_MSG_INSUFFICIENT_PATH).build(); } - String appId = segments.get(0).toString(); String tableId = getTableIdFromPathSegments(segments); - // Now construct the whole path. String wholePath = constructPathFromSegments(segments); byte[] fileBlob; String contentType; Long contentLength; try { - List entities = DbTableFileInfo.queryForEntity(tableId, wholePath, cc); + List entities = DbTableFileInfo.queryForEntity(odkClientVersion, tableId, wholePath, cc); if (entities.size() > 1) { Log log = LogFactory.getLog(DbTableFileInfo.class); log.error("more than one entity for appId: " + appId + ", tableId: " + tableId @@ -157,20 +151,11 @@ public Response getFile(@PathParam("filePath") List segments, @Quer @Path("{filePath:.*}") @Consumes({MediaType.MEDIA_TYPE_WILDCARD}) // because we want to get the whole path - public Response putFile(@Context HttpServletRequest req, @PathParam("filePath") List segments, @GZIP byte[] content) throws IOException, ODKTaskLockException { - if (segments.size() <= 1) { + public Response putFile(@Context HttpServletRequest req, @PathParam("appId") String appId, @PathParam("odkClientVersion") String odkClientVersion, @PathParam("filePath") List segments, @GZIP byte[] content) throws IOException, ODKTaskLockException { + if (segments.size() < 1) { return Response.status(Status.BAD_REQUEST).entity(FileService.ERROR_MSG_INSUFFICIENT_PATH).build(); } - // First parse the url to get the correct app and table ids. - String appId = segments.get(0).toString(); String tableId = getTableIdFromPathSegments(segments); - if (!appId.equals("tables")) { - // For now we'll just do tables. eventually we want all apps. - // TODO: incorporate checking for apps - // TODO: incorporate checking for access control - return Response.status(Status.BAD_REQUEST).entity(FileService.ERROR_MSG_UNRECOGNIZED_APP_ID - + appId).build(); - } String wholePath = constructPathFromSegments(segments); String contentType = req.getContentType(); try { @@ -195,7 +180,7 @@ public Response putFile(@Context HttpServletRequest req, @PathParam("filePath") // // 1) Create an entry in the user friendly table. EntityCreator ec = new EntityCreator(); - DbTableFileInfoEntity tableFileInfoRow = ec.newTableFileInfoEntity(tableId, wholePath, + DbTableFileInfoEntity tableFileInfoRow = ec.newTableFileInfoEntity(odkClientVersion, tableId, wholePath, userPermissions, cc); String rowUri = tableFileInfoRow.getId(); @@ -208,17 +193,17 @@ public Response putFile(@Context HttpServletRequest req, @PathParam("filePath") BlobEntitySet instance = dbTableFiles.newBlobEntitySet(rowUri, cc); // TODO: this being set to true is probably where some sort of versioning // should happen. - instance.addBlob(content, contentType, null, true, cc); + BlobSubmissionOutcome outcome = instance.addBlob(content, contentType, null, true, cc); // 3) persist the user-friendly table entry about the blob tableFileInfoRow.put(cc); String locationUrl = cc.getServerURL() + BasicConsts.FORWARDSLASH + ServletConsts.ODK_TABLES_SERVLET_BASE_PATH + BasicConsts.FORWARDSLASH - + FileService.SERVLET_PATH + BasicConsts.FORWARDSLASH + appId + BasicConsts.FORWARDSLASH - + wholePath; - - return Response.status(Status.CREATED).header("Location",locationUrl).build(); + + appId + BasicConsts.FORWARDSLASH + FileService.SERVLET_PATH + BasicConsts.FORWARDSLASH + + odkClientVersion + BasicConsts.FORWARDSLASH + wholePath; + return Response.status((outcome == BlobSubmissionOutcome.NEW_FILE_VERSION) ? Status.ACCEPTED : Status.CREATED) + .header("Location",locationUrl).build(); } catch (ODKDatastoreException e) { LOGGER.error(("ODKTables file upload persistence error: " + e.getMessage())); return Response.status(Status.INTERNAL_SERVER_ERROR).entity(ErrorConsts.PERSISTENCE_LAYER_PROBLEM + "\n" + e.getMessage()).build(); @@ -238,7 +223,7 @@ public Response putFile(@Context HttpServletRequest req, @PathParam("filePath") private String constructPathFromSegments(List segments) { // Now construct up the path from the segments. // We are NOT going to include the app id. Therefore if you upload a file - // with a path of appid/myDir/myFile.html, the path will be stored as + // with a path of /myDir/myFile.html, the path will be stored as // myDir/myFile.html. This is so that when you get the filename on the // manifest, it won't matter what is the root directory of your app on your // device. Otherwise you might have to strip the first path segment or do @@ -246,10 +231,6 @@ private String constructPathFromSegments(List segments) { StringBuilder sb = new StringBuilder(); int i = 0; for (PathSegment segment : segments) { - if (i == 0) { - i++; - continue; - } sb.append(segment.toString()); if (i < segments.size() - 1) { sb.append(PATH_DELIMITER); @@ -262,29 +243,36 @@ private String constructPathFromSegments(List segments) { /** * Retrieve the table id given the path. The first segment (position 0) is - * known to be the app id, as all files must be associated with an app id. Not - * all files must be associated with a table, however, so it parses to find - * the table id. Otherwise it returns the {@link DEFAULT_TABLE_ID}. + * a directory under /sdcard/opendatakit/{app_id}/, as all files must be + * associated with an app id. Not all files must be associated with a table, + * however, so it parses to find the table id. Otherwise it returns the + * {@link DEFAULT_TABLE_ID}. *

- * The convention is that any table id must be of the form: - * /appid/tables/tableid. So the 2nd position (0 indexed) will be the table - * idea if the first position is "tables". + * The convention is that any table-related file must be under: + * /tables/tableid + * OR a csv file: + * /assets/csv/tableid....csv + * + * So the 2nd position (0 indexed) will be the table + * id if the first position is "tables", and the 3rd + * position (0 indexed) will begin with the table id + * if it is a csv file under the assets/csv directory. * * @param segments * @return */ private String getTableIdFromPathSegments(List segments) { - String tableId; - if (segments.size() < 4) { - // Then we aren't a file name, b/c we're assuming it must be - // appid/tables/tableid/file - tableId = NO_TABLE_ID; - } else if (segments.get(1).toString().equals(TABLES_FOLDER)) { - // We have to see if it could be a tableId. If it can, then we assume it - // is a table id. Otherwise we give it the default tableId. - tableId = segments.get(2).toString(); - } else { - tableId = NO_TABLE_ID; + String tableId = DbTableFileInfo.NO_TABLE_ID; + if ((segments.size() >= 2) && segments.get(0).toString().equals(TABLES_FOLDER)) { + tableId = segments.get(1).toString(); + } else if ((segments.size() == 3) && segments.get(0).toString().equals(ASSETS_FOLDER)) { + if (segments.get(1).toString().equals(CSV_FOLDER)) { + String fileName = segments.get(2).toString(); + String splits[] = fileName.split("\\."); + if ( splits[splits.length-1].toLowerCase(Locale.ENGLISH).equals("csv") ) { + tableId = splits[0]; + } + } } return tableId; } diff --git a/src/main/java/org/opendatakit/aggregate/odktables/impl/api/InstanceFileServiceImpl.java b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/InstanceFileServiceImpl.java new file mode 100644 index 0000000000..1deac3382a --- /dev/null +++ b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/InstanceFileServiceImpl.java @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2012-2013 University of Washington + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.opendatakit.aggregate.odktables.impl.api; + +import java.io.IOException; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.PathSegment; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.ResponseBuilder; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.core.UriInfo; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jboss.resteasy.annotations.GZIP; +import org.opendatakit.aggregate.constants.ErrorConsts; +import org.opendatakit.aggregate.constants.ServletConsts; +import org.opendatakit.aggregate.odktables.api.FileService; +import org.opendatakit.aggregate.odktables.api.InstanceFileService; +import org.opendatakit.aggregate.odktables.exception.PermissionDeniedException; +import org.opendatakit.aggregate.odktables.relation.DbTableInstanceFiles; +import org.opendatakit.aggregate.odktables.rest.entity.TableRole.TablePermission; +import org.opendatakit.aggregate.odktables.security.TablesUserPermissions; +import org.opendatakit.common.datamodel.BinaryContentManipulator.BlobSubmissionOutcome; +import org.opendatakit.common.ermodel.BlobEntitySet; +import org.opendatakit.common.persistence.exception.ODKDatastoreException; +import org.opendatakit.common.persistence.exception.ODKEntityNotFoundException; +import org.opendatakit.common.persistence.exception.ODKTaskLockException; +import org.opendatakit.common.web.CallingContext; +import org.opendatakit.common.web.constants.BasicConsts; +import org.opendatakit.common.web.constants.HtmlConsts; + +public class InstanceFileServiceImpl implements InstanceFileService { + + private static final Log LOGGER = LogFactory.getLog(InstanceFileServiceImpl.class); + + /** + * String to stand in for those things in the app's root directory. + * + * NOTE: This cannot be null -- GAE doesn't like that! + */ + public static final String NO_TABLE_ID = ""; + + private static final String PATH_DELIMITER = "/"; + + private static final String ERROR_FILE_VERSION_DIFFERS = "File on server does not match file being uploaded. Aborting upload. "; + + /** + * The name of the folder that contains the files associated with a table in + * an app. + * + * @see #getTableIdFromPathSegments(List) + */ + private CallingContext cc; + private TablesUserPermissions userPermissions; + private UriInfo info; + private String appId; + private String tableId; + + public InstanceFileServiceImpl(String appId, String tableId, UriInfo info, TablesUserPermissions userPermissions, CallingContext cc) + throws ODKEntityNotFoundException, ODKDatastoreException { + this.cc = cc; + this.appId = appId; + this.tableId = tableId; + this.info = info; + this.userPermissions = userPermissions; + } + + @Override + @GET + @Path("{filePath:.*}") + // because we want to get the whole path + public Response getFile(@PathParam("filePath") List segments, @QueryParam(PARAM_AS_ATTACHMENT) String asAttachment, @QueryParam(PARAM_MD5_HASH_ONLY) String asMd5HashOnly) throws IOException { + // The appId and tableId are from the surrounding TableService. + // The entire path from appId onward mimics the path to the instances + // directory By convention, this is: appid/tables/tableid/instances/rest/of/path. + // So we'll reclaim the tidbits and then reconstruct the entire path. + if (segments.size() <= 1) { + return Response.status(Status.BAD_REQUEST).entity(FileService.ERROR_MSG_INSUFFICIENT_PATH).build(); + } + // Now construct the whole path. + String wholePath = constructPathFromSegments(segments); + + byte[] fileBlob; + String contentType; + Long contentLength; + try { + DbTableInstanceFiles blobStore = new DbTableInstanceFiles(tableId, cc); + BlobEntitySet blobEntitySet = blobStore.getBlobEntitySet(wholePath, cc); + // We should only ever have one, as wholePath is the primary key. + if (blobEntitySet.getAttachmentCount(cc) > 1) { + return Response.status(Status.INTERNAL_SERVER_ERROR).entity("More than one file specified for: " + wholePath).build(); + } + if (blobEntitySet.getAttachmentCount(cc) < 1) { + return Response.status(Status.NOT_FOUND).entity("No file found for path: " + wholePath).build(); + } + // just the hash was requested -- return it. Perhaps this should be treated as an ETag? + if ( asMd5HashOnly != null && asMd5HashOnly.length() != 0 ) { + String md5 = blobEntitySet.getContentHash(1, cc); + ResponseBuilder rBuild = Response.ok(md5, MediaType.TEXT_PLAIN); + return rBuild.build(); + } + fileBlob = blobEntitySet.getBlob(1, cc); + contentType = blobEntitySet.getContentType(1, cc); + contentLength = blobEntitySet.getContentLength(1, cc); + } catch (ODKDatastoreException e) { + e.printStackTrace(); + return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Unable to retrieve attachment and access attributes for: " + wholePath).build(); + } + // And now prepare everything to be returned to the caller. + if (fileBlob != null && contentType != null && contentLength != null && contentLength != 0L) { + ResponseBuilder rBuild = Response.ok(fileBlob, contentType ); + if (asAttachment != null && !"".equals(asAttachment)) { + // Set the filename we're downloading to the disk. + rBuild.header(HtmlConsts.CONTENT_DISPOSITION, "attachment; " + "filename=\"" + wholePath + + "\""); + } + return rBuild.build(); + } else { + return Response.status(Status.NOT_FOUND).entity("File content not yet available for: " + wholePath).build(); + } + } + + @Override + @POST + @Path("{filePath:.*}") + @Consumes({MediaType.MEDIA_TYPE_WILDCARD}) + // because we want to get the whole path + public Response putFile(@Context HttpServletRequest req, @PathParam("filePath") List segments, @GZIP byte[] content) throws IOException, ODKTaskLockException { + if (segments.size() <= 1) { + return Response.status(Status.BAD_REQUEST).entity(FileService.ERROR_MSG_INSUFFICIENT_PATH).build(); + } + String wholePath = constructPathFromSegments(segments); + String contentType = req.getContentType(); + try { + userPermissions.checkPermission(appId, tableId, TablePermission.WRITE_ROW); + // We are going to store the file in two tables: 1) a user-friendly table + // that relates an app and table id to the name of a file; 2) a table + // that holds the actual blob. + // + // Table 1 is represented by DbTableFileInfo. Each row of this table + // contains a uri, appid, tableid, and pathToFile. + // Table 2 is a BlobEntitySet. The top level URI of this blob entity set + // is the uri from table 1. Each blob set here has a single attachment + // count of 1--the blob of the file itself. The pathToFile of this + // attachment is null. + // + // So, now that we have retrieved the file from the request, we have two + // things to do: 1) create an entry in the user-friendly table so we can + // bet a uri. 2) add the file to the blob entity set, using the top level + // uri as the row uri from table 1. + // + // 1) Create an entry in the user friendly table. + // Although this is called an entity set, it in fact represents a single + // file, because we have chosen to use it this way in this case. + DbTableInstanceFiles blobStore = new DbTableInstanceFiles(tableId, cc); + BlobEntitySet instance = blobStore.newBlobEntitySet(wholePath, cc); + // TODO: this being set to true is probably where some sort of versioning + // should happen. + BlobSubmissionOutcome outcome = instance.addBlob(content, contentType, null, false, cc); + if ( outcome == BlobSubmissionOutcome.NEW_FILE_VERSION ) { + return Response.status(Status.BAD_REQUEST).entity(ERROR_FILE_VERSION_DIFFERS + "\n" + wholePath).build(); + } + + String locationUrl = cc.getServerURL() + BasicConsts.FORWARDSLASH + + ServletConsts.ODK_TABLES_SERVLET_BASE_PATH + BasicConsts.FORWARDSLASH + + wholePath; + + return Response.status(Status.CREATED).header("Location",locationUrl).build(); + } catch (ODKDatastoreException e) { + LOGGER.error(("ODKTables file upload persistence error: " + e.getMessage())); + return Response.status(Status.INTERNAL_SERVER_ERROR).entity(ErrorConsts.PERSISTENCE_LAYER_PROBLEM + "\n" + e.getMessage()).build(); + } catch (PermissionDeniedException e) { + LOGGER.error(("ODKTables file upload permissions error: " + e.getMessage())); + return Response.status(Status.UNAUTHORIZED).entity("Permission denied").build(); + } + } + + /** + * Construct the path for the file. This is the entire path excluding the app + * id. + * + * @param segments + * @return + */ + private String constructPathFromSegments(List segments) { + // Now construct up the path from the segments. + // We are NOT going to include the app id. Therefore if you upload a file + // with a path of appid/myDir/myFile.html, the path will be stored as + // myDir/myFile.html. This is so that when you get the filename on the + // manifest, it won't matter what is the root directory of your app on your + // device. Otherwise you might have to strip the first path segment or do + // something similar. + StringBuilder sb = new StringBuilder(); + sb.append("tables"); + sb.append(PATH_DELIMITER); + sb.append(tableId); + sb.append(PATH_DELIMITER); + sb.append("instances"); + sb.append(PATH_DELIMITER); + int i = 0; + for (PathSegment segment : segments) { + sb.append(segment.toString()); + if (i < segments.size() - 1) { + sb.append(PATH_DELIMITER); + } + i++; + } + String wholePath = sb.toString(); + return wholePath; + } + +} diff --git a/src/main/java/org/opendatakit/aggregate/odktables/impl/api/TableServiceImpl.java b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/TableServiceImpl.java index c8d784cfaa..1250669a9a 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/impl/api/TableServiceImpl.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/TableServiceImpl.java @@ -35,6 +35,7 @@ import org.opendatakit.aggregate.odktables.TableManager; import org.opendatakit.aggregate.odktables.api.DataService; import org.opendatakit.aggregate.odktables.api.DiffService; +import org.opendatakit.aggregate.odktables.api.InstanceFileService; import org.opendatakit.aggregate.odktables.api.PropertiesService; import org.opendatakit.aggregate.odktables.api.TableAclService; import org.opendatakit.aggregate.odktables.api.TableService; @@ -146,6 +147,12 @@ public TableAclService getAcl(@PathParam("appId") String appId, @PathParam("tabl return service; } + @Override + public InstanceFileService getInstanceFiles(@PathParam("appId") String appId, @PathParam("tableId") String tableId) throws ODKDatastoreException { + InstanceFileService service = new InstanceFileServiceImpl(appId, tableId, info, userPermissions, cc); + return service; + } + @Override public Response getDefinition(@PathParam("appId") String appId, @PathParam("tableId") String tableId) throws ODKDatastoreException, PermissionDeniedException, ODKTaskLockException { // TODO: permissions stuff for a table, perhaps? or just at the row level? diff --git a/src/main/java/org/opendatakit/aggregate/odktables/relation/DbLogTable.java b/src/main/java/org/opendatakit/aggregate/odktables/relation/DbLogTable.java index fe8ea16601..952ddb9624 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/relation/DbLogTable.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/relation/DbLogTable.java @@ -62,24 +62,32 @@ private DbLogTable(String namespace, String tableName, List fields, C // Whether or not this DbTable Row is deleted. public static final DataField DELETED = new DataField("_DELETED", DataType.BOOLEAN, false); + + // limited to 10 characters + public static final DataField FILTER_TYPE = new DataField(TableConstants.FILTER_TYPE.toUpperCase(), + DataType.STRING, true, 10L); + // limited to 50 characters + public static final DataField FILTER_VALUE = new DataField(TableConstants.FILTER_VALUE.toUpperCase(), + DataType.STRING, true, 50L).setIndexable(IndexType.HASH); // The FormId of the form that was in use when this record was last saved. + // limited to 50 characters public static final DataField FORM_ID = new DataField(TableConstants.FORM_ID.toUpperCase(), - DataType.STRING, true); + DataType.STRING, true, 50L); // The locale that was active when this record was last saved. + // limited to 10 characters public static final DataField LOCALE = new DataField(TableConstants.LOCALE.toUpperCase(), - DataType.STRING, true); + DataType.STRING, true, 10L); + // limited to 10 characters + public static final DataField SAVEPOINT_TYPE = new DataField(TableConstants.SAVEPOINT_TYPE.toUpperCase(), + DataType.STRING, true, 10L); // nanoseconds at the time the form was saved (on client). + // limited to 40 characters public static final DataField SAVEPOINT_TIMESTAMP = new DataField( - TableConstants.SAVEPOINT_TIMESTAMP.toUpperCase(), DataType.STRING, true); + TableConstants.SAVEPOINT_TIMESTAMP.toUpperCase(), DataType.STRING, true, 40L); // the creator of this row, as reported by the device (may be a remote SMS user) public static final DataField SAVEPOINT_CREATOR = new DataField( TableConstants.SAVEPOINT_CREATOR.toUpperCase(), DataType.STRING, true); - // Access control filters accessible only on server (these may be useless) - public static final DataField FILTER_TYPE = new DataField("_FILTER_TYPE", DataType.STRING, true); - public static final DataField FILTER_VALUE = new DataField("_FILTER_VALUE", DataType.STRING, true) - .setIndexable(IndexType.HASH); - private static final List dataFields; static { dataFields = new ArrayList(); @@ -94,14 +102,13 @@ private DbLogTable(String namespace, String tableName, List fields, C dataFields.add(DELETED); // common metadata transmitted between server and device + dataFields.add(FILTER_TYPE); + dataFields.add(FILTER_VALUE); dataFields.add(FORM_ID); dataFields.add(LOCALE); + dataFields.add(SAVEPOINT_TYPE); dataFields.add(SAVEPOINT_TIMESTAMP); dataFields.add(SAVEPOINT_CREATOR); - - // Access control filters accessible only on server (these may be useless) - dataFields.add(FILTER_TYPE); - dataFields.add(FILTER_VALUE); } private static final EntityConverter converter = new EntityConverter(); diff --git a/src/main/java/org/opendatakit/aggregate/odktables/relation/DbTable.java b/src/main/java/org/opendatakit/aggregate/odktables/relation/DbTable.java index 213cc5cbc7..1187a771dc 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/relation/DbTable.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/relation/DbTable.java @@ -64,18 +64,30 @@ private DbTable(String namespace, String tableName, List fields, Call public static final DataField CREATE_USER = new DataField("_CREATE_USER", DataType.STRING, true); public static final DataField LAST_UPDATE_USER = new DataField("_LAST_UPDATE_USER", DataType.STRING, true); - public static final DataField FILTER_TYPE = new DataField("_FILTER_TYPE", DataType.STRING, true); - public static final DataField FILTER_VALUE = new DataField("_FILTER_VALUE", DataType.STRING, true) - .setIndexable(IndexType.HASH); public static final DataField DELETED = new DataField("_DELETED", DataType.BOOLEAN, false); + // limited to 10 characters + public static final DataField FILTER_TYPE = new DataField(TableConstants.FILTER_TYPE.toUpperCase(), + DataType.STRING, true, 10L); + // limited to 50 characters + public static final DataField FILTER_VALUE = new DataField(TableConstants.FILTER_VALUE.toUpperCase(), + DataType.STRING, true, 50L).setIndexable(IndexType.HASH); + // The FormId of the form that was in use when this record was last saved. + // limited to 50 characters public static final DataField FORM_ID = new DataField(TableConstants.FORM_ID.toUpperCase(), - DataType.STRING, true); + DataType.STRING, true, 50L); + // The locale that was active when this record was last saved. + // limited to 10 characters public static final DataField LOCALE = new DataField(TableConstants.LOCALE.toUpperCase(), - DataType.STRING, true); - // nanoseconds + DataType.STRING, true, 10L); + // limited to 10 characters + public static final DataField SAVEPOINT_TYPE = new DataField(TableConstants.SAVEPOINT_TYPE.toUpperCase(), + DataType.STRING, true, 10L); + // nanoseconds at the time the form was saved (on client). + // limited to 40 characters public static final DataField SAVEPOINT_TIMESTAMP = new DataField( - TableConstants.SAVEPOINT_TIMESTAMP.toUpperCase(), DataType.STRING, true); + TableConstants.SAVEPOINT_TIMESTAMP.toUpperCase(), DataType.STRING, true, 40L); + // the creator of this row, as reported by the device (may be a remote SMS user) public static final DataField SAVEPOINT_CREATOR = new DataField( TableConstants.SAVEPOINT_CREATOR.toUpperCase(), DataType.STRING, true); @@ -91,14 +103,13 @@ private DbTable(String namespace, String tableName, List fields, Call dataFields.add(DELETED); // common metadata transmitted between server and device + dataFields.add(FILTER_TYPE); + dataFields.add(FILTER_VALUE); dataFields.add(FORM_ID); dataFields.add(LOCALE); + dataFields.add(SAVEPOINT_TYPE); dataFields.add(SAVEPOINT_TIMESTAMP); dataFields.add(SAVEPOINT_CREATOR); - - // Access control filters accessible only on server (these may be useless) - dataFields.add(FILTER_TYPE); - dataFields.add(FILTER_VALUE); } private static final EntityConverter converter = new EntityConverter(); diff --git a/src/main/java/org/opendatakit/aggregate/odktables/relation/DbTableFileInfo.java b/src/main/java/org/opendatakit/aggregate/odktables/relation/DbTableFileInfo.java index 7da35a5d97..015684419b 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/relation/DbTableFileInfo.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/relation/DbTableFileInfo.java @@ -56,6 +56,13 @@ private DbTableFileInfo(String namespace, String tableName, List fiel super(namespace, tableName, fields, cc); } + /** + * String to stand in for those things in the app's root directory. + * + * NOTE: This cannot be null -- GAE doesn't like that! + */ + public static final String NO_TABLE_ID = ""; + // these are the user-friendly names that are displayed when the user // views the contents of this table on the server. public static final String UI_ONLY_FILENAME_HEADING = "_FILENAME"; @@ -68,7 +75,8 @@ private DbTableFileInfo(String namespace, String tableName, List fiel // be displayed to the user on the server. The underscore will be truncated. // Jul 17, 2013--kind of just playing nice with the underscore thing for now // as I add in proper file sync support. - public static final DataField TABLE_ID = new DataField("_TABLE_ID", DataType.STRING, true); + public static final DataField ODK_CLIENT_VERSION = new DataField("_ODK_CLIENT_VERSION", DataType.STRING, true, 10L); + public static final DataField TABLE_ID = new DataField("_TABLE_ID", DataType.STRING, true, 80L); public static final DataField PATH_TO_FILE = new DataField("_PATH_TO_FILE", DataType.STRING, true, 5120L); @@ -82,13 +90,14 @@ private DbTableFileInfo(String namespace, String tableName, List fiel // fields. public static final List exposedColumnNames; - public static final String RELATION_NAME = "TABLE_FILE_INFO2"; + public static final String RELATION_NAME = "TABLE_FILE_INFO3"; // the list of the datafields/columns private static final List dataFields; static { dataFields = new ArrayList(); // can be null because we're + dataFields.add(ODK_CLIENT_VERSION); dataFields.add(TABLE_ID); dataFields.add(PATH_TO_FILE); // and add the things from DbTable @@ -96,6 +105,7 @@ private DbTableFileInfo(String namespace, String tableName, List fiel // TODO: do the appropriate time stamping and things. // populate the list with all the column names List columns = new ArrayList(); + columns.add(ODK_CLIENT_VERSION); columns.add(TABLE_ID); columns.add(PATH_TO_FILE); exposedColumnNames = Collections.unmodifiableList(columns); @@ -131,6 +141,14 @@ public void setTableId(String value) { e.set(TABLE_ID, value); } + public String getOdkClientVersion() { + return e.getString(ODK_CLIENT_VERSION); + } + + public void setOdkClientVersion(String value) { + e.set(ODK_CLIENT_VERSION, value); + } + public String getPathToFile() { return e.getString(PATH_TO_FILE); } @@ -182,10 +200,12 @@ public static DbTableFileInfoEntity createNewEntity(CallingContext cc) /** * Returns all the entries. */ - public static List queryForApp(CallingContext cc) + public static List queryForAppLevelFiles(String odkClientVersion, CallingContext cc) throws ODKDatastoreException { Query query = getRelation(cc).query("DbTableFileInfo.queryForApp()", cc); + query.addFilter(TABLE_ID, FilterOperation.EQUAL, NO_TABLE_ID); + query.addFilter(ODK_CLIENT_VERSION, FilterOperation.EQUAL, odkClientVersion); List list = query.execute(); List results = new ArrayList(); @@ -198,7 +218,39 @@ public static List queryForApp(CallingContext cc) /** * Returns the entries for the passed in table id. */ - public static List queryForTableId(String tableId, CallingContext cc) + public static List queryForTableIdFiles(String odkClientVersion, String tableId, CallingContext cc) + throws ODKDatastoreException { + + Query query = getRelation(cc).query("DbTableFileInfo.queryForTableId()", cc); + query.addFilter(TABLE_ID, FilterOperation.EQUAL, tableId); + query.addFilter(ODK_CLIENT_VERSION, FilterOperation.EQUAL, odkClientVersion); + + List list = query.execute(); + List results = new ArrayList(); + for (Entity e : list) { + results.add(new DbTableFileInfoEntity(e)); + } + return results; + } + + public static List queryForAllOdkClientVersionsOfAppLevelFiles(CallingContext cc) + throws ODKDatastoreException { + + Query query = getRelation(cc).query("DbTableFileInfo.queryForTableId()", cc); + query.addFilter(TABLE_ID, FilterOperation.EQUAL, NO_TABLE_ID); + + List list = query.execute(); + List results = new ArrayList(); + for (Entity e : list) { + results.add(new DbTableFileInfoEntity(e)); + } + return results; + } + + /** + * Returns the entries across all odkClientVersions for the passed in table id. + */ + public static List queryForAllOdkClientVersionsOfTableIdFiles(String tableId, CallingContext cc) throws ODKDatastoreException { Query query = getRelation(cc).query("DbTableFileInfo.queryForTableId()", cc); @@ -212,11 +264,12 @@ public static List queryForTableId(String tableId, Callin return results; } - public static List queryForEntity(String tableId, String wholePath, + public static List queryForEntity(String odkClientVersion, String tableId, String wholePath, CallingContext cc) throws ODKDatastoreException { Query query = getRelation(cc).query("DbTableFileInfo.queryForEntity()", cc); query.addFilter(TABLE_ID, FilterOperation.EQUAL, tableId); + query.addFilter(ODK_CLIENT_VERSION, FilterOperation.EQUAL, odkClientVersion); query.addFilter(PATH_TO_FILE, FilterOperation.EQUAL, wholePath); List list = query.execute(); diff --git a/src/main/java/org/opendatakit/aggregate/odktables/relation/DbTableFiles.java b/src/main/java/org/opendatakit/aggregate/odktables/relation/DbTableFiles.java index 04b8a392d1..7d1f75b180 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/relation/DbTableFiles.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/relation/DbTableFiles.java @@ -46,50 +46,4 @@ public DbTableFiles(CallingContext cc) throws ODKDatastoreException { super(BLOB_RELATION_NAME, cc); } - /* - * public static DbTableFiles getBlobRelationSet(CallingContext cc) throws - * ODKDatastoreException { return new DbTableFiles(cc); } - */ - - /* - * /*** these will be the column names in the datastore table *** public - * static final String TABLE_ID = "TABLE_ENTRY_ID"; // this will house the - * different key possibilities, such as // listview, boxview, etc. public - * static final String FILE_KEY = "FILE_KEY"; // will house the actual file. - * public static final String FILE = "FILE"; - * - * - * // name of the table private static final String RELATION_NAME = - * "TABLE_FILES"; - * - * // the "columns" themselves. columns are fields within an // entity (row). - * private static final List dataFields; - * - * // do the one time constructor stuff, but do it in a static block // so it - * is more efficient. a la Dylan in DbColumn.java. static { dataFields = new - * ArrayList(); dataFields.add(new DataField(TABLE_ID, - * DataType.STRING, false) .setIndexable(IndexType.HASH)); dataFields.add(new - * DataField(FILE_KEY, DataType.STRING, false)); dataFields.add(new - * DataField(FILE, DataType.BINARY, false)); } - * - * public static Relation getRelation(CallingContext cc) throws - * ODKDatastoreException { Relation relation = new Relation(RUtil.NAMESPACE, - * RELATION_NAME, dataFields, cc); return relation; } - * - * /** Returns the rows (entities) for a given table id. - * - * @param tableId - * - * @param cc - * - * @return - * - * @throws ODKDatastoreException - * - * public static List query(String tableId, CallingContext cc) throws - * ODKDatastoreException { return - * getRelation(cc).query("DbTableFiles.query()", cc).equal(TABLE_ID, tableId) - * .execute(); } - */ - } diff --git a/src/main/java/org/opendatakit/aggregate/odktables/relation/DbTableInstanceFiles.java b/src/main/java/org/opendatakit/aggregate/odktables/relation/DbTableInstanceFiles.java new file mode 100644 index 0000000000..f564ee3f0e --- /dev/null +++ b/src/main/java/org/opendatakit/aggregate/odktables/relation/DbTableInstanceFiles.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2012-2013 University of Washington + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.opendatakit.aggregate.odktables.relation; + +import org.opendatakit.common.ermodel.AbstractBlobRelationSet; +import org.opendatakit.common.persistence.exception.ODKDatastoreException; +import org.opendatakit.common.web.CallingContext; + +/** + * This represents the datastore table that holds files for the instance data + * of a table. There is one of these for each table in order to simplify + * DBA operations. + *

+ * These files are going to be stored using an AbstractBlobRelationSet. This + * handles most of the mechanics of storing arbitrarily large binary files. It + * is based on BlobRelationSetTest. + * + * @author mitchellsundt@gmail.com + * + */ +public class DbTableInstanceFiles extends AbstractBlobRelationSet { + + public DbTableInstanceFiles(String dataTableName, CallingContext cc) throws ODKDatastoreException { + super(dataTableName, cc); + } + +} diff --git a/src/main/java/org/opendatakit/aggregate/odktables/relation/EntityConverter.java b/src/main/java/org/opendatakit/aggregate/odktables/relation/EntityConverter.java index fb53ce12f3..a375357ecf 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/relation/EntityConverter.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/relation/EntityConverter.java @@ -372,13 +372,14 @@ public Row toRowFromLogTable(Entity entity, List colu row.setDataETagAtModification(entity.getString(DbLogTable.DATA_ETAG_AT_MODIFICATION)); row.setCreateUser(entity.getString(DbLogTable.CREATE_USER)); row.setLastUpdateUser(entity.getString(DbLogTable.LAST_UPDATE_USER)); - row.setFilterScope(getDbLogTableFilterScope(entity)); row.setDeleted(entity.getBoolean(DbLogTable.DELETED)); + row.setFilterScope(getDbLogTableFilterScope(entity)); row.setFormId(entity.getString(DbLogTable.FORM_ID)); row.setLocale(entity.getString(DbLogTable.LOCALE)); - row.setSavepointCreator(entity.getString(DbLogTable.SAVEPOINT_CREATOR)); + row.setSavepointType(entity.getString(DbLogTable.SAVEPOINT_TYPE)); row.setSavepointTimestamp(entity.getString(DbLogTable.SAVEPOINT_TIMESTAMP)); + row.setSavepointCreator(entity.getString(DbLogTable.SAVEPOINT_CREATOR)); row.setValues(getRowValues(entity, columns)); return row; diff --git a/src/main/java/org/opendatakit/aggregate/odktables/relation/EntityCreator.java b/src/main/java/org/opendatakit/aggregate/odktables/relation/EntityCreator.java index 401d1ee17c..e4648d624d 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/relation/EntityCreator.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/relation/EntityCreator.java @@ -122,19 +122,22 @@ public DbColumnDefinitionsEntity newColumnEntity(String tableId, String schemaET /** * Create a new {@link DbTableFileInfo} entity. * + * @param odkClientVersion * @param tableId * @param pathToFile * @param cc * @return * @throws ODKDatastoreException */ - public DbTableFileInfoEntity newTableFileInfoEntity(String tableId, String pathToFile, + public DbTableFileInfoEntity newTableFileInfoEntity(String odkClientVersion, + String tableId, String pathToFile, TablesUserPermissionsImpl userPermissions, CallingContext cc) throws ODKDatastoreException { // first do some preliminary checks Validate.notEmpty(pathToFile); // TODO: check permissions to create a file... DbTableFileInfoEntity entity = DbTableFileInfo.createNewEntity(cc); + entity.setOdkClientVersion(odkClientVersion); entity.setTableId(tableId); entity.setPathToFile(pathToFile); @@ -265,13 +268,14 @@ public DbTableAclEntity newTableAclEntity(String tableId, Scope scope, TableRole } public void setRowFields(Entity row, String rowETag, String dataETagAtModification, - String lastUpdateUser, Scope filterScope, - boolean deleted, String formId, String locale, + String lastUpdateUser, boolean deleted, + Scope filterScope, String formId, String locale, String savepointType, String savepointTimestamp, String savepointCreator, Map values, List columns) throws BadColumnNameException { row.set(DbTable.ROW_ETAG, rowETag); row.set(DbTable.DATA_ETAG_AT_MODIFICATION, dataETagAtModification); row.set(DbTable.LAST_UPDATE_USER, lastUpdateUser); + row.set(DbTable.DELETED, deleted); // if filterScope is null, don't change the value // if filterScope is the empty scope, set both filter type and value to null @@ -292,10 +296,9 @@ public void setRowFields(Entity row, String rowETag, String dataETagAtModificati } } - row.set(DbTable.DELETED, deleted); - row.set(DbTable.FORM_ID, formId); row.set(DbTable.LOCALE, locale); + row.set(DbTable.SAVEPOINT_TYPE, savepointType); row.set(DbTable.SAVEPOINT_TIMESTAMP, savepointTimestamp); row.set(DbTable.SAVEPOINT_CREATOR, savepointCreator); @@ -378,15 +381,16 @@ public Entity newLogEntity(DbLogTable logTable, String dataETagAtModification, entity.set(DbLogTable.DATA_ETAG_AT_MODIFICATION, dataETagAtModification); entity.set(DbLogTable.CREATE_USER, row.getString(DbTable.CREATE_USER)); entity.set(DbLogTable.LAST_UPDATE_USER, row.getString(DbTable.LAST_UPDATE_USER)); - entity.set(DbLogTable.FILTER_TYPE, row.getString(DbTable.FILTER_TYPE)); - entity.set(DbLogTable.FILTER_VALUE, row.getString(DbTable.FILTER_VALUE)); entity.set(DbLogTable.DELETED, row.getBoolean(DbTable.DELETED)); // common metadata - entity.set(DbLogTable.SAVEPOINT_CREATOR, row.getString(DbTable.SAVEPOINT_CREATOR)); + entity.set(DbLogTable.FILTER_TYPE, row.getString(DbTable.FILTER_TYPE)); + entity.set(DbLogTable.FILTER_VALUE, row.getString(DbTable.FILTER_VALUE)); entity.set(DbLogTable.FORM_ID, row.getString(DbTable.FORM_ID)); entity.set(DbLogTable.LOCALE, row.getString(DbTable.LOCALE)); + entity.set(DbLogTable.SAVEPOINT_TYPE, row.getString(DbTable.SAVEPOINT_TYPE)); entity.set(DbLogTable.SAVEPOINT_TIMESTAMP, row.getString(DbTable.SAVEPOINT_TIMESTAMP)); + entity.set(DbLogTable.SAVEPOINT_CREATOR, row.getString(DbTable.SAVEPOINT_CREATOR)); for (DbColumnDefinitionsEntity column : columns) { if (column.getIsUnitOfRetention()) { diff --git a/src/main/java/org/opendatakit/aggregate/odktables/rest/ConflictType.java b/src/main/java/org/opendatakit/aggregate/odktables/rest/ConflictType.java new file mode 100644 index 0000000000..7d46a4f486 --- /dev/null +++ b/src/main/java/org/opendatakit/aggregate/odktables/rest/ConflictType.java @@ -0,0 +1,64 @@ +package org.opendatakit.aggregate.odktables.rest; + + +/** + * This class stores ints that should be stored in the + * client-side (device) data table's {@link TableConstants#CONFLICT_TYPE} + * column. This column represents the reason that the rows are labeled as conflict. + *

+ * There are essentially three scenarios to consider. All of which will have + * the rows' {@link TableConstants#SYNC_STATE} set to {@link SyncState.conflicting}: + *

    + *
  1. Two users have modified the same row, one has synched, the other pull + * those changes
  2. + *
      + *
    • In this case neither row has been deleted. The local row will have + * {@link TableConstants#CONFLICT_TYPE} equal to + * {@link ConflictType#LOCAL_UPDATED_UPDATED_VALUES}, and the server + * row will have {@link TableConstants#CONFLICT_TYPE} equal to + * {@link ConflictType#SERVER_UPDATED_UPDATED_VALUES}. + *
    + *
  3. The row has been deleted on the server, and the local user has edited + * their local version
  4. + *
      + *
    • In this case the server row is considered deleted. Its + * {@link TableConstants#CONFLICT_TYPE} row will be set to + * {@link ConflictType#SERVER_DELETED_OLD_VALUES}. The values in + * that row will be the contents of that row on the server at the time of + * deletion. The local row, which had been edited and been in + * {@link SyncState.updating} before the sync (and thus why it was + * not deleted outright), will have {@link TableConstants#CONFLICT_TYPE} + * set to {@link ConflictType#LOCAL_UPDATED_UPDATED_VALUES}. + * TODO: what happens if both versions are deleted, but the row versions were + * different? Is this case handled appropriately? Unclear... + *
    + *
  5. The local row has been deleted, but a newer version has + * been updated on the server
  6. + *
      + *
    • In this case the local row will have + * {@link TableConstants#CONFLICT_TYPE} equal to + * {@link ConflictType#LOCAL_DELETED_OLD_VALUES}. It will have the + * values that were in the row at the time of deletion. These may differ + * from the last synced version of the row--i.e. updates may have been + * performed before deletion, meaning the sync state moved from + * {@link SyncState.rest} to {@link SyncState.updating} to + * {@link SyncState.deleting}. The server row meanwhile will have + * {@link TableConstants#CONFLICT_TYPE} set to + * {@link ConflictType#SERVER_UPDATED_UPDATED_VALUES}. The + * contents of this row will be the latest version of the updated row on the + * server. + *
    + *
+ * @author sudar.sam@gmail.com + * + */ +public class ConflictType { + + public static final int LOCAL_DELETED_OLD_VALUES = 0; + public static final int LOCAL_UPDATED_UPDATED_VALUES = 1; + public static final int SERVER_DELETED_OLD_VALUES = 2; + public static final int SERVER_UPDATED_UPDATED_VALUES = 3; + + private ConflictType() { + } +} \ No newline at end of file diff --git a/src/main/java/org/opendatakit/aggregate/odktables/rest/SavepointTypeManipulator.java b/src/main/java/org/opendatakit/aggregate/odktables/rest/SavepointTypeManipulator.java new file mode 100644 index 0000000000..8481596087 --- /dev/null +++ b/src/main/java/org/opendatakit/aggregate/odktables/rest/SavepointTypeManipulator.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2014 University of Washington + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.opendatakit.aggregate.odktables.rest; + +/** + * A manipulator for the 'savepoint_type' column. + * This is a manipulator largely because this field is + * transmitted between the client and the server and + * enums are difficult to extend. + * + * @author mitchellsundt@gmail.com + * + */ +public class SavepointTypeManipulator { + // values are limited to 10 characters or less! + private static final String INCOMPLETE = "INCOMPLETE"; + private static final String COMPLETE = "COMPLETE"; + + public static boolean isCheckpoint(String savepointType) { + return savepointType == null; + } + + public static boolean isIncomplete(String savepointType) { + return INCOMPLETE.equals(savepointType); + } + + public static boolean isComplete(String savepointType) { + return COMPLETE.equals(savepointType); + } + + public static boolean isSavepointType(String savepointType) { + return isCheckpoint(savepointType) || + isIncomplete(savepointType) || + isComplete(savepointType); + } + + public static String incomplete() { + return INCOMPLETE; + } + + public static String complete() { + return COMPLETE; + } + + public static String checkpoint() { + return null; + } +} diff --git a/src/main/java/org/opendatakit/aggregate/odktables/rest/SyncState.java b/src/main/java/org/opendatakit/aggregate/odktables/rest/SyncState.java new file mode 100644 index 0000000000..bce2e0671d --- /dev/null +++ b/src/main/java/org/opendatakit/aggregate/odktables/rest/SyncState.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2012 University of Washington + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.opendatakit.aggregate.odktables.rest; + +/** + * The contents of the SYNC_STATE column. The state of the table with regards + * to sync'ing to the webserver. + *

+ * NB: Lowercase due to considerations regarding javascript. + *

+ *

+ * Here is a brief overview of the rules for transitions between states on basic + * write operations: + * + *

+ * insert:
+ *     state = INSERTING
+ *
+ * update:
+ *     if state == REST:
+ *        state = UPDATING
+ *
+ * delete:
+ *     if state == REST or state == UPDATING:
+ *        state = DELETING
+ *        don't actually delete yet (defer until sync)
+ *     else if state == INSERTING:
+ *        actually delete
+ * 
+ *

+ * + * @author sudar.sam@gmail.com + * + */ +public enum SyncState { + rest, inserting, updating, deleting, conflicting; +} diff --git a/src/main/java/org/opendatakit/aggregate/odktables/rest/TableConstants.java b/src/main/java/org/opendatakit/aggregate/odktables/rest/TableConstants.java index e54bc63771..0546e70365 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/rest/TableConstants.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/rest/TableConstants.java @@ -34,23 +34,58 @@ */ public class TableConstants { - // TODO: should probably have an Aggregate Column object instead that will - // allow you to specify type here. - - /* - * These are the names of the shared columns. Included here so that they can - * be accessed directly by aggregate. + /** + * Columns in the User data table. */ - // tablename is chosen by user... + /** + * ID is the primary key, as experienced by the user. There may be + * multiple rows with this ID due to conflicts and checkpoint states. + */ public static final String ID = "_id"; + + /** + * ROW_ETAG defines the the server entity tag (version) of the row. + * This is controlled by the server and changes by the client are always + * ignored. + */ public static final String ROW_ETAG = "_row_etag"; + + /************************************************ + * These columns are only on the client device. + ************************************************/ + + /** + * Client side only. One of the {@link SyncState} values. + */ public static final String SYNC_STATE = "_sync_state"; + + /** + * Client side only. One of the {@ConflictType} constants. + */ public static final String CONFLICT_TYPE = "_conflict_type"; + /************************************************ + * These columns are common across server and client + ************************************************/ + + /** + * Contains the type attribute of a Scope. The Scope describes the access + * controls on this row. Changes to this value on the device may or may not + * be applied during a sync action (TBD). + */ + public static final String FILTER_TYPE = "_filter_type"; /** - * (savepoint_timestamp, savepoint_creator, savepoint_type, form_id, locale) + * Contains the value attribute of a Scope. The Scope describes the access + * controls on this row. Changes to this value on the device may or may not + * be applied during a sync action (TBD). + */ + public static final String FILTER_VALUE = "_filter_value"; + + /** + * (form_id, locale, savepoint_type, savepoint_timestamp, savepoint_creator) * are the tuple written and managed by ODK Survey when a record is updated. + * * ODK Tables needs to update these appropriately when a cell is directly * edited based upon whether or not the table is 'form-managed' or not. If * form-managed, and direct cell editing is allowed, it should set @@ -60,28 +95,22 @@ public class TableConstants { * The value of 'savepoint_creator' is the user that is making the change. This * may be a remote SMS user. * - * Note that the value of 'savepoint_type' must be 'COMPLETE' in order for ODK - * Tables to sync the values up to the server, otherwise there is a conflict - * resolution step that is enforced on the device. I.e., this is a requirement - * before a data row can move off of a device. Hence, we do not need to record - * the value of 'savepoint_type' on the server, because it will always be - * 'COMPLETE'. + * In a table being sync'd, the value of 'savepoint_type' for each row must + * either be INCOMPLETE or COMPLETE. There is a clean-up step before a sync + * during which the user is prompted to select whether to mark a checkpoint + * (null) savepoint-type as INCOMPLETE or to remove it. Only once that is done + * can the sync proceed. * * In contrast, the row security management, savepoint, form, locale, sync * state, and conflict resolution fields are metadata and are not directly * exposed to the user. */ + public static final String SAVEPOINT_TYPE = "_savepoint_type"; public static final String SAVEPOINT_TIMESTAMP = "_savepoint_timestamp"; public static final String SAVEPOINT_CREATOR = "_savepoint_creator"; public static final String FORM_ID = "_form_id"; public static final String LOCALE = "_locale"; - /* - * savepoint_type is never sent to the server since it should always be - * 'COMPLETE' - */ - public static final String SAVEPOINT_TYPE = "_savepoint_type"; - /** * This set contains the names of the metadata columns that are present in all * ODKTables data tables. The data in these columns needs to be synched to the @@ -99,14 +128,16 @@ public class TableConstants { static { SHARED_COLUMN_NAMES = new HashSet(); CLIENT_ONLY_COLUMN_NAMES = new HashSet(); + SHARED_COLUMN_NAMES.add(FILTER_TYPE); + SHARED_COLUMN_NAMES.add(FILTER_VALUE); + SHARED_COLUMN_NAMES.add(SAVEPOINT_TYPE); SHARED_COLUMN_NAMES.add(SAVEPOINT_TIMESTAMP); SHARED_COLUMN_NAMES.add(SAVEPOINT_CREATOR); SHARED_COLUMN_NAMES.add(FORM_ID); SHARED_COLUMN_NAMES.add(LOCALE); - CLIENT_ONLY_COLUMN_NAMES.add(ID); - CLIENT_ONLY_COLUMN_NAMES.add(SAVEPOINT_TYPE); - CLIENT_ONLY_COLUMN_NAMES.add(SYNC_STATE); + CLIENT_ONLY_COLUMN_NAMES.add(ID); // somewhat of a misnomer -- this is transmitted and serves as a PK to the record. CLIENT_ONLY_COLUMN_NAMES.add(ROW_ETAG); // somewhat of a misnomer -- this is transmitted, but never overwrites server. + CLIENT_ONLY_COLUMN_NAMES.add(SYNC_STATE); CLIENT_ONLY_COLUMN_NAMES.add(CONFLICT_TYPE); } diff --git a/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/Row.java b/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/Row.java index ebf5f40366..33fd45a96b 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/Row.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/Row.java @@ -45,32 +45,41 @@ public class Row { @Element(required = false) private String lastUpdateUser; + /** + * OdkTables metadata column. + */ @Element(required = false) - private Scope filterScope; + private String formId; /** * OdkTables metadata column. */ @Element(required = false) - private String savepointCreator; + private String locale; /** * OdkTables metadata column. */ @Element(required = false) - private String formId; + private String savepointType; /** * OdkTables metadata column. */ @Element(required = false) - private String locale; + private String savepointTimestamp; /** * OdkTables metadata column. */ @Element(required = false) - private String savepointTimestamp; + private String savepointCreator; + + /** + * FilterScope is passed down to device. + */ + @Element(required = false) + private Scope filterScope; @ElementMap(entry = "entry", key = "column", attribute = true, inline = true) private Map values; @@ -83,13 +92,16 @@ public class Row { * @param values */ public static Row forInsert(String rowId, String formId, String locale, - String savepointTimestamp, String savepointCreator, Map values) { + String savepointType, String savepointTimestamp, String savepointCreator, + Scope filterScope, Map values) { Row row = new Row(); row.rowId = rowId; row.formId = formId; row.locale = locale; + row.savepointType = savepointType; row.savepointTimestamp = savepointTimestamp; row.savepointCreator = savepointCreator; + row.filterScope = filterScope; row.values = values; return row; } @@ -103,14 +115,17 @@ public static Row forInsert(String rowId, String formId, String locale, * @param values */ public static Row forUpdate(String rowId, String rowETag, String formId, - String locale, String savepointTimestamp, String savepointCreator, Map values) { + String locale, String savepointType, String savepointTimestamp, + String savepointCreator, Scope filterScope, Map values) { Row row = new Row(); row.rowId = rowId; row.rowETag = rowETag; row.formId = formId; row.locale = locale; + row.savepointType = savepointType; row.savepointTimestamp = savepointTimestamp; row.savepointCreator = savepointCreator; + row.filterScope = filterScope; row.values = values; return row; } @@ -122,12 +137,13 @@ public Row() { this.deleted = false; this.createUser = null; this.lastUpdateUser = null; - this.filterScope = null; // data coming up from client - this.savepointCreator = null; this.formId = null; this.locale = null; + this.savepointType = null; this.savepointTimestamp = null; + this.savepointCreator = null; + this.filterScope = null; this.values = new HashMap(); } @@ -171,6 +187,10 @@ public String getLocale() { return this.locale; } + public String getSavepointType() { + return this.savepointType; + } + public String getSavepointTimestamp() { return this.savepointTimestamp; } @@ -219,6 +239,10 @@ public void setLocale(String locale) { this.locale = locale; } + public void setSavepointType(String savepointType) { + this.savepointType = savepointType; + } + public void setValues(final Map values) { this.values = values; } @@ -248,6 +272,7 @@ public int hashCode() { result = prime * result + ((savepointCreator == null) ? 0 : savepointCreator.hashCode()); result = prime * result + ((formId == null) ? 0 : formId.hashCode()); result = prime * result + ((locale == null) ? 0 : locale.hashCode()); + result = prime * result + ((savepointType == null) ? 0 : savepointType.hashCode()); result = prime * result + ((savepointTimestamp == null) ? 0 : savepointTimestamp.hashCode()); result = prime * result + ((values == null) ? 0 : values.hashCode()); return result; @@ -278,6 +303,8 @@ public boolean equals(Object obj) { .equals(other.savepointCreator)) && (formId == null ? other.formId == null : formId.equals(other.formId)) && (locale == null ? other.locale == null : locale.equals(other.locale)) + && (savepointType == null ? other.savepointType == null : savepointType + .equals(other.savepointType)) && (savepointTimestamp == null ? other.savepointTimestamp == null : savepointTimestamp .equals(other.savepointTimestamp)) && (values == null ? other.values == null : values.equals(other.values)); @@ -304,6 +331,8 @@ public boolean hasMatchingSignificantFieldValues(Row other) { && (filterScope == null ? other.filterScope == null : filterScope.equals(other.filterScope)) && (formId == null ? other.formId == null : formId.equals(other.formId)) && (locale == null ? other.locale == null : locale.equals(other.locale)) + && (savepointType == null ? other.savepointType == null : savepointType + .equals(other.savepointType)) && (savepointTimestamp == null ? other.savepointTimestamp == null : savepointTimestamp .equals(other.savepointTimestamp)) && (savepointCreator == null ? other.savepointCreator == null : savepointCreator @@ -337,6 +366,8 @@ public String toString() { builder.append(formId); builder.append(", locale="); builder.append(locale); + builder.append(", savepointType="); + builder.append(savepointType); builder.append(", savepointTimestamp="); builder.append(savepointTimestamp); builder.append(", savepointCreator="); diff --git a/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/Scope.java b/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/Scope.java index 4bdafbf2be..d19d4817d6 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/Scope.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/Scope.java @@ -27,6 +27,24 @@ public class Scope { EMPTY_SCOPE.initFields(null, null); } + public static Scope asScope(String filterType, String filterValue) { + if (filterType != null) { + Scope.Type type = Scope.Type.valueOf(filterType); + if (filterType.equals(Scope.Type.DEFAULT)) { + return new Scope(Scope.Type.DEFAULT, null); + } else { + return new Scope(type, filterValue); + } + } else { + return Scope.EMPTY_SCOPE; + } + } + + /** + * Type of Scope. + * + * Limited to 10 characters + */ public enum Type { DEFAULT, USER, GROUP, } diff --git a/src/main/java/org/opendatakit/aggregate/odktables/rest/serialization/TextPlainHttpMessageConverter.java b/src/main/java/org/opendatakit/aggregate/odktables/rest/serialization/TextPlainHttpMessageConverter.java new file mode 100644 index 0000000000..568d2693fc --- /dev/null +++ b/src/main/java/org/opendatakit/aggregate/odktables/rest/serialization/TextPlainHttpMessageConverter.java @@ -0,0 +1,22 @@ +package org.opendatakit.aggregate.odktables.rest.serialization; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.http.MediaType; +import org.springframework.http.converter.StringHttpMessageConverter; + +public class TextPlainHttpMessageConverter extends StringHttpMessageConverter { + + @Override + public List getSupportedMediaTypes() { + // The default implementation of this includes */* according to the doc at: + // http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/http/converter/StringHttpMessageConverter.html + // This is bad, as we are using xml message converters as well. So instead + // we're going to ONLY support Text/Plain. + List onlyType = new ArrayList(); + onlyType.add(MediaType.TEXT_PLAIN); + return onlyType; + } + +} diff --git a/src/main/java/org/opendatakit/aggregate/server/ServerDataServiceImpl.java b/src/main/java/org/opendatakit/aggregate/server/ServerDataServiceImpl.java index 3061bfc4cc..72a96f824f 100644 --- a/src/main/java/org/opendatakit/aggregate/server/ServerDataServiceImpl.java +++ b/src/main/java/org/opendatakit/aggregate/server/ServerDataServiceImpl.java @@ -474,7 +474,7 @@ public TableContentsForFilesClient getFileInfoContents(String tableId) if (table == null) { // you couldn't find the table throw new ODKEntityNotFoundException(); } - List entities = DbTableFileInfo.queryForTableId(table.getTableId(), cc); + List entities = DbTableFileInfo.queryForAllOdkClientVersionsOfTableIdFiles(table.getTableId(), cc); DbTableFiles dbTableFiles = new DbTableFiles(cc); ArrayList completedSummaries = new ArrayList(); @@ -483,14 +483,15 @@ public TableContentsForFilesClient getFileInfoContents(String tableId) if (blobEntitySet.getAttachmentCount(cc) != 1) { continue; } + String odkClientVersion = entry.getOdkClientVersion(); String downloadUrl = cc.getServerURL() + BasicConsts.FORWARDSLASH + ServletConsts.ODK_TABLES_SERVLET_BASE_PATH + BasicConsts.FORWARDSLASH - + FileService.SERVLET_PATH + BasicConsts.FORWARDSLASH + appId + BasicConsts.FORWARDSLASH - + entry.getPathToFile() + "?" + FileService.PARAM_AS_ATTACHMENT + "=true"; + + appId + BasicConsts.FORWARDSLASH + FileService.SERVLET_PATH + BasicConsts.FORWARDSLASH + + odkClientVersion + BasicConsts.FORWARDSLASH + entry.getPathToFile() + "?" + FileService.PARAM_AS_ATTACHMENT + "=true"; FileSummaryClient sum = new FileSummaryClient(entry.getPathToFile(), blobEntitySet.getContentType(1, cc), blobEntitySet.getContentLength(1, cc), - entry.getId(), tableId, downloadUrl); + entry.getId(), odkClientVersion, tableId, downloadUrl); completedSummaries.add(sum); } tcc.nonMediaFiles = completedSummaries; diff --git a/src/main/java/org/opendatakit/aggregate/server/ServerOdkTablesUtil.java b/src/main/java/org/opendatakit/aggregate/server/ServerOdkTablesUtil.java index 65a3e14ba2..6830255023 100644 --- a/src/main/java/org/opendatakit/aggregate/server/ServerOdkTablesUtil.java +++ b/src/main/java/org/opendatakit/aggregate/server/ServerOdkTablesUtil.java @@ -218,7 +218,7 @@ public static RowClient createOrUpdateRow(String appId, String tableId, String r * @return * @throws ODKDatastoreException */ - public static FileSummaryClient getFileSummaryClientFromRow(Row row, String tableId, + public static FileSummaryClient getFileSummaryClientFromRow(Row row, String odkClientVersion, String tableId, DbTableFiles blobSetRelation, CallingContext cc) throws ODKDatastoreException { String filename = blobSetRelation.getBlobEntitySet( row.getValues().get(DbTableFileInfo.PATH_TO_FILE), cc).getUnrootedFilename(1, cc); @@ -226,7 +226,7 @@ public static FileSummaryClient getFileSummaryClientFromRow(Row row, String tabl String contentType = blobSetRelation.getBlobEntitySet(filename, cc).getContentType(1, cc); String id = row.getRowId(); String downloadUrl = null; - FileSummaryClient summary = new FileSummaryClient(filename, contentType, contentLength, id, tableId, downloadUrl); + FileSummaryClient summary = new FileSummaryClient(filename, contentType, contentLength, id, odkClientVersion, tableId, downloadUrl); return summary; } } diff --git a/src/main/java/org/opendatakit/aggregate/servlet/OdkTablesFileUploadFromPhoneServlet.java b/src/main/java/org/opendatakit/aggregate/servlet/OdkTablesFileUploadFromPhoneServlet.java deleted file mode 100644 index be6d6b9675..0000000000 --- a/src/main/java/org/opendatakit/aggregate/servlet/OdkTablesFileUploadFromPhoneServlet.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.opendatakit.aggregate.servlet; - -/** - * This servlet uploads a file from a phone and adds it to the database. Unlike - * {@link OdkTablesTableFileUploadServlet}, it does not present a UI but - * instead expects the phone to handle it correctly. - * @author sudar.sam@gmail.com - * - */ -public class OdkTablesFileUploadFromPhoneServlet extends ServletUtilBase { - - /** - * - */ - private static final long serialVersionUID = -4535033734935035680L; - -} diff --git a/src/main/java/org/opendatakit/aggregate/servlet/OdkTablesManifestServlet.java b/src/main/java/org/opendatakit/aggregate/servlet/OdkTablesManifestServlet.java deleted file mode 100644 index 58d90019c3..0000000000 --- a/src/main/java/org/opendatakit/aggregate/servlet/OdkTablesManifestServlet.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2012-2013 University of Washington - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.opendatakit.aggregate.servlet; - -import java.io.IOException; -import java.io.PrintWriter; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.opendatakit.aggregate.ContextFactory; -import org.opendatakit.aggregate.client.exception.RequestFailureException; -import org.opendatakit.aggregate.constants.ServletConsts; -import org.opendatakit.aggregate.odktables.entity.serialization.OdkTablesKeyValueManifestManager; -import org.opendatakit.aggregate.odktables.exception.PermissionDeniedException; -import org.opendatakit.aggregate.odktables.impl.api.ServiceUtils; -import org.opendatakit.aggregate.odktables.security.TablesUserPermissionsImpl; -import org.opendatakit.aggregate.server.ServerPreferencesProperties; -import org.opendatakit.common.persistence.client.exception.DatastoreFailureException; -import org.opendatakit.common.persistence.exception.ODKDatastoreException; -import org.opendatakit.common.persistence.exception.ODKTaskLockException; -import org.opendatakit.common.security.client.exception.AccessDeniedException; -import org.opendatakit.common.web.CallingContext; -import org.opendatakit.common.web.constants.HtmlConsts; - -/** - * Handles the request for a Based on XFormsManifestServlet. - * - * @author sudars - * - */ -public class OdkTablesManifestServlet extends ServletUtilBase { - - /** - * - */ - private static final long serialVersionUID = -6054191018860216729L; - - /** - * URI from base - */ - public static final String ADDR = "tableKeyValueManifest"; - - @Override - public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { - ServiceUtils.examineRequest(getServletContext(), req); - CallingContext cc = ContextFactory.getCallingContext(this, req); - // reclaim the parameters, which in this case is just the - // tableId - String tableId = getParameter(req, ServletConsts.TABLE_ID); - if (tableId == null) { - errorMissingKeyParam(resp); - return; - } - TablesUserPermissionsImpl userPermissions; - String appId; - try { - userPermissions = new TablesUserPermissionsImpl(cc.getCurrentUser().getUriUser(), cc); - appId = ServerPreferencesProperties.getOdkTablesAppId(cc); - } catch (ODKDatastoreException e) { - e.printStackTrace(); - datastoreError(resp); - return; - } catch (ODKTaskLockException e) { - e.printStackTrace(); - errorRetreivingData(resp); - return; - } catch (PermissionDeniedException e) { - e.printStackTrace(); - errorRetreivingData(resp); - return; - } - OdkTablesKeyValueManifestManager mm = new OdkTablesKeyValueManifestManager(appId, tableId, userPermissions, cc); - - String manifest; - try { - manifest = mm.getManifest(); - } catch (AccessDeniedException e) { - e.printStackTrace(); - errorRetreivingData(resp); - return; - } catch (RequestFailureException e) { - e.printStackTrace(); - errorRetreivingData(resp); - return; - } catch (DatastoreFailureException e) { - e.printStackTrace(); - datastoreError(resp); - return; - } catch (PermissionDeniedException e) { - e.printStackTrace(); - errorRetreivingData(resp); - return; - } - - resp.setCharacterEncoding(HtmlConsts.UTF8_ENCODE); - resp.setContentType(HtmlConsts.RESP_TYPE_JSON); - addOpenDataKitHeaders(resp); - PrintWriter out = resp.getWriter(); - out.write(manifest); - } - -} diff --git a/src/main/java/org/opendatakit/aggregate/servlet/OdkTablesTableFileDownloadServlet.java b/src/main/java/org/opendatakit/aggregate/servlet/OdkTablesTableFileDownloadServlet.java deleted file mode 100644 index 4d541ece41..0000000000 --- a/src/main/java/org/opendatakit/aggregate/servlet/OdkTablesTableFileDownloadServlet.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (C) 2012-2013 University of Washington - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.opendatakit.aggregate.servlet; - -import java.io.IOException; -import java.io.OutputStream; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.opendatakit.aggregate.ContextFactory; -import org.opendatakit.aggregate.constants.ErrorConsts; -import org.opendatakit.aggregate.constants.ServletConsts; -import org.opendatakit.aggregate.constants.common.UIConsts; -import org.opendatakit.aggregate.odktables.impl.api.ServiceUtils; -import org.opendatakit.aggregate.odktables.relation.DbTableFiles; -import org.opendatakit.common.ermodel.BlobEntitySet; -import org.opendatakit.common.persistence.exception.ODKDatastoreException; -import org.opendatakit.common.web.CallingContext; -import org.opendatakit.common.web.constants.HtmlConsts; - -/** - * The servlet that handles the downloading of table files. Based mostly on - * XFormsDownloadServlet. - * - * @author sudar.sam@gmail.com - * - */ -public class OdkTablesTableFileDownloadServlet extends ServletUtilBase { - - /** - * - */ - private static final long serialVersionUID = -4681728139240108291L; - - @SuppressWarnings("unused") - private static final Log logger = LogFactory.getLog(OdkTablesTableFileDownloadServlet.class); - - /** - * URI from base. - */ - public static final String ADDR = UIConsts.TABLE_FILE_DOWNLOAD_SERVLET_ADDR; - - /** - * Handler for the HTTP Get request. - */ - @Override - public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { - ServiceUtils.examineRequest(getServletContext(), req); - CallingContext cc = ContextFactory.getCallingContext(this, req); - - // verify the parameters you expect are there - String keyString = getParameter(req, ServletConsts.BLOB_KEY); - String downloadAsAttachmentString = getParameter(req, ServletConsts.AS_ATTACHMENT); - - if (keyString == null) { - sendErrorNotEnoughParams(resp); - return; - } - - // here I am diverging from XFormsDownloadServlet. They have a Submission - // Key. - // I am planning on the key passed in being the key to the file in the - // datastore. - // So, let's hope that works. - - byte[] imageBlob; - String unrootedFileName; - String contentType; - // THIS COULD BE A PROBLEM--MAYBE SHOULD BE SETTING TYPE WITH - // AN HTML TYPE AS FROM HtmlConsts.RESP_TYPE... - Long contentLength; - - try { - - DbTableFiles files = new DbTableFiles(cc); - BlobEntitySet blobSet = files.getBlobEntitySet(keyString, cc); - // should only ever be one, b/c each of the files is in an - // entity set of 1. - if (blobSet.getAttachmentCount(cc) != 1) { - resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, - "Unexpectedly non-unary attachment count"); - } - imageBlob = blobSet.getBlob(1, cc); - unrootedFileName = files.getBlobEntitySet(keyString, cc).getUnrootedFilename(1, cc); - contentType = files.getBlobEntitySet(keyString, cc).getContentType(1, cc); - contentLength = files.getBlobEntitySet(keyString, cc).getContentLength(1, cc); - } catch (ODKDatastoreException e) { - e.printStackTrace(); - resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, - "Unable to retrieve attachment and access attributes"); - return; - } - - if (imageBlob != null) { - // not sure why they set to jpeg - if (contentType == null) { - resp.setContentType(HtmlConsts.RESP_TYPE_IMAGE_JPEG); - } else { - resp.setContentType(contentType); - } - if (contentLength != null) { - resp.setContentLength(contentLength.intValue()); - } - addOpenDataKitHeaders(resp); - - if (downloadAsAttachmentString != null && !"".equals(downloadAsAttachmentString)) { - // set the file name if we're downloading to the disk - if (unrootedFileName != null) { - resp.addHeader(HtmlConsts.CONTENT_DISPOSITION, "attachment; filename=\"" - + unrootedFileName + "\""); - } - } - OutputStream os = resp.getOutputStream(); - os.write(imageBlob); - } else { - resp.setCharacterEncoding(HtmlConsts.UTF8_ENCODE); - resp.setContentType(HtmlConsts.RESP_TYPE_PLAIN); - addOpenDataKitHeaders(resp); - resp.getWriter().print(ErrorConsts.NO_IMAGE_EXISTS); - } - - } - -} diff --git a/src/test/java/org/opendatakit/aggregate/odktables/AuthFilterTest.java b/src/test/java/org/opendatakit/aggregate/odktables/AuthFilterTest.java index 57094bdfff..ef2a8f03f4 100644 --- a/src/test/java/org/opendatakit/aggregate/odktables/AuthFilterTest.java +++ b/src/test/java/org/opendatakit/aggregate/odktables/AuthFilterTest.java @@ -26,6 +26,7 @@ import org.junit.Ignore; import org.junit.Test; import org.opendatakit.aggregate.odktables.exception.PermissionDeniedException; +import org.opendatakit.aggregate.odktables.rest.SavepointTypeManipulator; import org.opendatakit.aggregate.odktables.rest.entity.Row; import org.opendatakit.aggregate.odktables.rest.entity.Scope; import org.opendatakit.aggregate.odktables.rest.entity.Scope.Type; @@ -163,8 +164,8 @@ public void testCheckFilterSucceedsDefaultScope() throws PermissionDeniedExcepti ODKDatastoreException { am.deleteAcl(currentUserScope); am.setAcl(currentUserScope, TableRole.FILTERED_READER); - Row row = Row.forInsert("1", T.form_id_1, T.locale_1, - T.savepoint_timestamp_1, T.savepoint_creator_1, Maps. newHashMap()); + Row row = Row.forInsert("1", T.form_id_1, T.locale_1, SavepointTypeManipulator.complete(), + T.savepoint_timestamp_1, T.savepoint_creator_1, Scope.EMPTY_SCOPE, Maps. newHashMap()); row.setFilterScope(new Scope(Type.DEFAULT, null)); assertTrue(af.hasFilterScope(TablePermission.READ_ROW, row.getRowId(), row.getFilterScope())); } @@ -174,8 +175,8 @@ public void testCheckFilterFailsEmptyScope() throws PermissionDeniedException, ODKDatastoreException { am.deleteAcl(currentUserScope); am.setAcl(currentUserScope, TableRole.FILTERED_READER); - Row row = Row.forInsert("1", T.form_id_1, T.locale_1, - T.savepoint_timestamp_1, T.savepoint_creator_1, Maps. newHashMap()); + Row row = Row.forInsert("1", T.form_id_1, T.locale_1, SavepointTypeManipulator.complete(), + T.savepoint_timestamp_1, T.savepoint_creator_1, Scope.EMPTY_SCOPE, Maps. newHashMap()); row.setFilterScope(Scope.EMPTY_SCOPE); assertFalse(af.hasFilterScope(TablePermission.READ_ROW, row.getRowId(), row.getFilterScope())); } @@ -185,8 +186,8 @@ public void testCheckFilterSucceedsUserScope() throws PermissionDeniedException, ODKDatastoreException { am.deleteAcl(currentUserScope); am.setAcl(currentUserScope, TableRole.FILTERED_READER); - Row row = Row.forInsert("1", T.form_id_1, T.locale_1, - T.savepoint_timestamp_1, T.savepoint_creator_1, Maps. newHashMap()); + Row row = Row.forInsert("1", T.form_id_1, T.locale_1, SavepointTypeManipulator.complete(), + T.savepoint_timestamp_1, T.savepoint_creator_1, Scope.EMPTY_SCOPE, Maps. newHashMap()); row.setFilterScope(currentUserScope); assertTrue(af.hasFilterScope(TablePermission.UNFILTERED_READ, row.getRowId(), row.getFilterScope())); } @@ -196,8 +197,8 @@ public void testCheckFilterFailsUserScope() throws PermissionDeniedException, ODKDatastoreException { am.deleteAcl(currentUserScope); am.setAcl(currentUserScope, TableRole.FILTERED_READER); - Row row = Row.forInsert("1", T.form_id_1, T.locale_1, - T.savepoint_timestamp_1, T.savepoint_creator_1, Maps. newHashMap()); + Row row = Row.forInsert("1", T.form_id_1, T.locale_1, SavepointTypeManipulator.complete(), + T.savepoint_timestamp_1, T.savepoint_creator_1, Scope.EMPTY_SCOPE, Maps. newHashMap()); row.setFilterScope(new Scope(Type.USER, currentUserScope.getValue() + "diff")); assertFalse(af.hasFilterScope(TablePermission.UNFILTERED_READ, row.getRowId(), row.getFilterScope())); } diff --git a/src/test/java/org/opendatakit/aggregate/odktables/DataManagerTest.java b/src/test/java/org/opendatakit/aggregate/odktables/DataManagerTest.java index a13e9e7706..476897b3ba 100644 --- a/src/test/java/org/opendatakit/aggregate/odktables/DataManagerTest.java +++ b/src/test/java/org/opendatakit/aggregate/odktables/DataManagerTest.java @@ -33,6 +33,7 @@ import org.opendatakit.aggregate.odktables.exception.ETagMismatchException; import org.opendatakit.aggregate.odktables.exception.InconsistentStateException; import org.opendatakit.aggregate.odktables.exception.PermissionDeniedException; +import org.opendatakit.aggregate.odktables.rest.SavepointTypeManipulator; import org.opendatakit.aggregate.odktables.rest.entity.Row; import org.opendatakit.aggregate.odktables.rest.entity.Scope; import org.opendatakit.aggregate.odktables.rest.entity.Scope.Type; @@ -211,7 +212,8 @@ public void testGetRows() throws ODKDatastoreException, ODKTaskLockException, @Test public void testGetRowNullSafe() throws ODKEntityPersistException, ODKDatastoreException, ODKTaskLockException, BadColumnNameException, ETagMismatchException, PermissionDeniedException, InconsistentStateException { - Row added = Row.forInsert(T.Data.DYLAN.getId(), T.form_id_1, T.locale_1, T.savepoint_timestamp_1, T.savepoint_creator_1, T.Data.DYLAN.getValues()); + Row added = Row.forInsert(T.Data.DYLAN.getId(), T.form_id_1, T.locale_1, SavepointTypeManipulator.complete(), + T.savepoint_timestamp_1, T.savepoint_creator_1, Scope.EMPTY_SCOPE, T.Data.DYLAN.getValues()); Row expected = dm.insertOrUpdateRow(added); Row actual = dm.getRow(T.Data.DYLAN.getId()); assertEquals(expected, actual); @@ -284,7 +286,8 @@ public void testUpdateRowDoesNotChangeFilter() throws ODKEntityPersistException, expected.setFilterScope(new Scope(Type.USER, T.user)); expected = dm.insertOrUpdateRow(expected); Row actual = Row.forUpdate(expected.getRowId(), expected.getRowETag(), - T.form_id_1, T.locale_1, T.savepoint_timestamp_1, T.savepoint_creator_1, + T.form_id_1, T.locale_1, SavepointTypeManipulator.complete(), + T.savepoint_timestamp_1, T.savepoint_creator_1, new Scope(Type.USER, T.user), Maps. newHashMap()); actual = dm.insertOrUpdateRow(actual); expected.setRowETag(actual.getRowETag()); @@ -301,7 +304,8 @@ public void testUpdateRowDoesChangeFilter() throws ODKEntityNotFoundException, row = dm.insertOrUpdateRow(row); Row actual = Row .forUpdate(row.getRowId(), row.getRowETag(), - T.form_id_1, T.locale_1, T.savepoint_timestamp_1, T.savepoint_creator_1, + T.form_id_1, T.locale_1, SavepointTypeManipulator.complete(), + T.savepoint_timestamp_1, T.savepoint_creator_1, Scope.EMPTY_SCOPE, Maps. newHashMap()); actual.setFilterScope(Scope.EMPTY_SCOPE); actual = dm.insertOrUpdateRow(actual); @@ -318,7 +322,8 @@ public void testUpdateRowBadColumnName() throws ODKEntityPersistException, row = dm.insertOrUpdateRow(row); Map values = Maps.newHashMap(); values.put(T.Columns.name + "diff", "value"); - row = Row.forUpdate(row.getRowId(), row.getRowETag(), T.form_id_1, T.locale_1, T.savepoint_timestamp_1, T.savepoint_creator_1, values); + row = Row.forUpdate(row.getRowId(), row.getRowETag(), T.form_id_1, T.locale_1, SavepointTypeManipulator.complete(), + T.savepoint_timestamp_1, T.savepoint_creator_1, Scope.EMPTY_SCOPE, values); @SuppressWarnings("unused") Row actual = dm.insertOrUpdateRow(row); } @@ -432,19 +437,23 @@ private List setupTestRows() throws ODKEntityPersistException, ODKDatastore Map values = Maps.newHashMap(); List rows = new ArrayList(); - Row row = Row.forInsert("1", T.form_id_1, T.locale_1, T.savepoint_timestamp_1, T.savepoint_creator_1, values); + Row row = Row.forInsert("1", T.form_id_1, T.locale_1, SavepointTypeManipulator.complete(), + T.savepoint_timestamp_1, T.savepoint_creator_1, Scope.EMPTY_SCOPE, values); row.setFilterScope(new Scope(Type.DEFAULT, null)); rows.add(row); - row = Row.forInsert("2", T.form_id_2, T.locale_2, T.savepoint_timestamp_2, T.savepoint_creator_2, values); + row = Row.forInsert("2", T.form_id_2, T.locale_2, SavepointTypeManipulator.complete(), + T.savepoint_timestamp_2, T.savepoint_creator_2, Scope.EMPTY_SCOPE, values); row.setFilterScope(new Scope(Type.USER, T.user)); rows.add(row); - row = Row.forInsert("3", T.form_id_1, T.locale_1, T.savepoint_timestamp_1, T.savepoint_creator_1, values); + row = Row.forInsert("3", T.form_id_1, T.locale_1, SavepointTypeManipulator.complete(), + T.savepoint_timestamp_1, T.savepoint_creator_1, Scope.EMPTY_SCOPE, values); row.setFilterScope(new Scope(Type.GROUP, T.group)); rows.add(row); - row = Row.forInsert("4", T.form_id_2, T.locale_2, T.savepoint_timestamp_2, T.savepoint_creator_2, values); + row = Row.forInsert("4", T.form_id_2, T.locale_2, SavepointTypeManipulator.complete(), + T.savepoint_timestamp_2, T.savepoint_creator_2, Scope.EMPTY_SCOPE, values); row.setFilterScope(Scope.EMPTY_SCOPE); rows.add(row); diff --git a/src/test/java/org/opendatakit/aggregate/odktables/T.java b/src/test/java/org/opendatakit/aggregate/odktables/T.java index af811ed246..2ad6fb2404 100644 --- a/src/test/java/org/opendatakit/aggregate/odktables/T.java +++ b/src/test/java/org/opendatakit/aggregate/odktables/T.java @@ -23,11 +23,12 @@ import org.junit.Ignore; import org.opendatakit.aggregate.odktables.rest.KeyValueStoreConstants; +import org.opendatakit.aggregate.odktables.rest.SavepointTypeManipulator; import org.opendatakit.aggregate.odktables.rest.TableConstants; import org.opendatakit.aggregate.odktables.rest.entity.Column; import org.opendatakit.aggregate.odktables.rest.entity.OdkTablesKeyValueStoreEntry; import org.opendatakit.aggregate.odktables.rest.entity.Row; -import org.opendatakit.aggregate.odktables.rest.entity.TableType; +import org.opendatakit.aggregate.odktables.rest.entity.Scope; @Ignore public class T { @@ -177,7 +178,9 @@ public String getWeight() { columns.add(T.Columns.column_weight); columns2.add(T.Columns.column_name); columns2.add(T.Columns.column_age); - rows.add(Row.forInsert(T.Data.DYLAN.getId(), T.form_id_1, T.locale_1, T.savepoint_timestamp_1, T.savepoint_creator_1, T.Data.DYLAN.getValues())); - rows.add(Row.forInsert(T.Data.JOHN.getId(), T.form_id_2, T.locale_2, T.savepoint_timestamp_2, T.savepoint_creator_2, T.Data.JOHN.getValues())); + rows.add(Row.forInsert(T.Data.DYLAN.getId(), T.form_id_1, T.locale_1, SavepointTypeManipulator.complete(), + T.savepoint_timestamp_1, T.savepoint_creator_1, Scope.EMPTY_SCOPE, T.Data.DYLAN.getValues())); + rows.add(Row.forInsert(T.Data.JOHN.getId(), T.form_id_2, T.locale_2, SavepointTypeManipulator.complete(), + T.savepoint_timestamp_2, T.savepoint_creator_2, Scope.EMPTY_SCOPE, T.Data.JOHN.getValues())); }; } \ No newline at end of file diff --git a/src/test/java/org/opendatakit/aggregate/odktables/entity/SerializationTest.java b/src/test/java/org/opendatakit/aggregate/odktables/entity/SerializationTest.java index 99956cd942..abd283a9f4 100644 --- a/src/test/java/org/opendatakit/aggregate/odktables/entity/SerializationTest.java +++ b/src/test/java/org/opendatakit/aggregate/odktables/entity/SerializationTest.java @@ -26,6 +26,7 @@ import org.junit.Before; import org.junit.Test; import org.opendatakit.aggregate.odktables.T; +import org.opendatakit.aggregate.odktables.rest.SavepointTypeManipulator; import org.opendatakit.aggregate.odktables.rest.entity.Row; import org.opendatakit.aggregate.odktables.rest.entity.RowResource; import org.opendatakit.aggregate.odktables.rest.entity.RowResourceList; @@ -57,7 +58,8 @@ public void setUp() throws Exception { @Test public void testRowForUpdate() throws Exception { - Row expected = Row.forUpdate("1", "5", T.form_id_2, T.locale_2, T.savepoint_timestamp_2, T.savepoint_creator_2, T.Data.DYLAN.getValues()); + Row expected = Row.forUpdate("1", "5", T.form_id_2, T.locale_2, SavepointTypeManipulator.complete(), + T.savepoint_timestamp_2, T.savepoint_creator_2, Scope.EMPTY_SCOPE, T.Data.DYLAN.getValues()); serializer.write(expected, writer); String xml = writer.toString(); System.out.println(xml); @@ -67,7 +69,8 @@ public void testRowForUpdate() throws Exception { @Test public void testRowInsert() throws Exception { - Row expected = Row.forInsert("1", T.form_id_1, T.locale_1, T.savepoint_timestamp_1, T.savepoint_creator_1, T.Data.DYLAN.getValues()); + Row expected = Row.forInsert("1", T.form_id_1, T.locale_1, SavepointTypeManipulator.complete(), + T.savepoint_timestamp_1, T.savepoint_creator_1, Scope.EMPTY_SCOPE, T.Data.DYLAN.getValues()); serializer.write(expected, writer); String xml = writer.toString(); @@ -134,7 +137,8 @@ public void testTablePropertiesIsNotStrict() throws Exception { @Test public void testRowResource() throws Exception { Map values = T.Data.DYLAN.getValues(); - RowResource expected = new RowResource(Row.forInsert("1", T.form_id_1, T.locale_1, T.savepoint_timestamp_1, T.savepoint_creator_1, values)); + RowResource expected = new RowResource(Row.forInsert("1", T.form_id_1, T.locale_1, SavepointTypeManipulator.complete(), + T.savepoint_timestamp_1, T.savepoint_creator_1, Scope.EMPTY_SCOPE, values)); expected.setSelfUri("http://localhost:8080/odktables/tables/1/rows/1"); expected.setTableUri("http://localhost:8080/odktables/tables/1"); @@ -203,10 +207,12 @@ public void testTableAclResource() throws Exception { @Test public void testListOfRowResource() throws Exception { ArrayList expected = new ArrayList(); - RowResource one = new RowResource(Row.forInsert("1", T.form_id_1, T.locale_1, T.savepoint_timestamp_1, T.savepoint_creator_1, T.Data.DYLAN.getValues())); + RowResource one = new RowResource(Row.forInsert("1", T.form_id_1, T.locale_1, SavepointTypeManipulator.complete(), + T.savepoint_timestamp_1, T.savepoint_creator_1, Scope.EMPTY_SCOPE, T.Data.DYLAN.getValues())); one.setSelfUri("http://localhost/tables/1/rows/1"); one.setTableUri("http://localhost/tables/1"); - RowResource two = new RowResource(Row.forInsert("1", T.form_id_1, T.locale_1, T.savepoint_timestamp_1, T.savepoint_creator_1, T.Data.JOHN.getValues())); + RowResource two = new RowResource(Row.forInsert("1", T.form_id_1, T.locale_1, SavepointTypeManipulator.complete(), + T.savepoint_timestamp_1, T.savepoint_creator_1, Scope.EMPTY_SCOPE, T.Data.JOHN.getValues())); two.setSelfUri("http://localhost/tables/1/rows/2"); two.setTableUri("http://localhost/tables/1"); expected.add(one); From c8cc668fe2a1d3c9acb737967caa9b50825a6d8a Mon Sep 17 00:00:00 2001 From: mitchellsundt Date: Tue, 29 Apr 2014 10:07:13 -0700 Subject: [PATCH 07/13] Split out app-level, table-level, and instance attachment files. --- .../WEB-INF/applicationContext-security.xml | 1 + eclipse-aggregate-gae/war/WEB-INF/web.xml | 10 + .../aggregate/client/AggregateUI.java | 1 + .../OdkTablesManageAppLevelFilesSubTab.java | 95 ++++ .../OdkTablesManageInstanceFilesSubTab.java | 282 ++++++++++ .../OdkTablesManageTableFilesSubTab.java | 33 +- .../aggregate/client/OdkTablesTabUI.java | 67 ++- .../aggregate/client/RefreshTimer.java | 2 +- .../client/odktables/ServerDataService.java | 10 +- .../odktables/ServerDataServiceAsync.java | 6 +- .../TableContentsForFilesClient.java | 13 +- .../client/odktables/TableResourceClient.java | 17 + .../client/table/OdkTablesTableList.java | 4 +- .../table/OdkTablesViewAppLevelFileInfo.java | 156 ++++++ .../table/OdkTablesViewInstanceFileInfo.java | 207 +++++++ .../table/OdkTablesViewTableFileInfo.java | 22 +- .../OdkTablesShowTableFilesButton.java | 16 +- .../OdkTablesTableIdServletPopupButton.java | 70 +++ .../aggregate/constants/common/SubTabs.java | 4 +- .../aggregate/constants/common/UIConsts.java | 3 + .../odktables/FileManifestManager.java | 6 +- .../odktables/api/InstanceFileService.java | 21 +- .../aggregate/odktables/api/TableService.java | 16 +- .../odktables/entity/UtilTransforms.java | 1 + .../impl/api/InstanceFileServiceImpl.java | 220 +++++--- .../odktables/impl/api/TableServiceImpl.java | 2 + .../relation/DbTableInstanceFiles.java | 7 +- .../entity/OdkTablesFileManifestEntry.java | 10 + .../odktables/rest/entity/TableResource.java | 19 + .../server/ServerDataServiceImpl.java | 120 ++++- .../OdkTablesAppLevelFileUploadServlet.java | 504 ++++++++++++++++++ .../OdkTablesTableFileUploadServlet.java | 7 +- .../ermodel/AbstractBlobRelationSet.java | 25 + src/main/resources/common/security.properties | 2 +- .../odktables/entity/SerializationTest.java | 1 + 35 files changed, 1832 insertions(+), 148 deletions(-) create mode 100644 src/main/java/org/opendatakit/aggregate/client/OdkTablesManageAppLevelFilesSubTab.java create mode 100644 src/main/java/org/opendatakit/aggregate/client/OdkTablesManageInstanceFilesSubTab.java create mode 100644 src/main/java/org/opendatakit/aggregate/client/table/OdkTablesViewAppLevelFileInfo.java create mode 100644 src/main/java/org/opendatakit/aggregate/client/table/OdkTablesViewInstanceFileInfo.java create mode 100644 src/main/java/org/opendatakit/aggregate/client/widgets/OdkTablesTableIdServletPopupButton.java create mode 100644 src/main/java/org/opendatakit/aggregate/servlet/OdkTablesAppLevelFileUploadServlet.java diff --git a/eclipse-aggregate-gae/war/WEB-INF/applicationContext-security.xml b/eclipse-aggregate-gae/war/WEB-INF/applicationContext-security.xml index 84baf06231..98b05dee42 100644 --- a/eclipse-aggregate-gae/war/WEB-INF/applicationContext-security.xml +++ b/eclipse-aggregate-gae/war/WEB-INF/applicationContext-security.xml @@ -104,6 +104,7 @@ + diff --git a/eclipse-aggregate-gae/war/WEB-INF/web.xml b/eclipse-aggregate-gae/war/WEB-INF/web.xml index 7c379b4e74..c6028a780e 100644 --- a/eclipse-aggregate-gae/war/WEB-INF/web.xml +++ b/eclipse-aggregate-gae/war/WEB-INF/web.xml @@ -267,6 +267,16 @@ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ /submission + + + appLevelFileUpload + org.opendatakit.aggregate.servlet.OdkTablesAppLevelFileUploadServlet + + + appLevelFileUpload + /appLevelFileUpload + + tableFileUpload diff --git a/src/main/java/org/opendatakit/aggregate/client/AggregateUI.java b/src/main/java/org/opendatakit/aggregate/client/AggregateUI.java index ae1cb2a452..502b8a3704 100644 --- a/src/main/java/org/opendatakit/aggregate/client/AggregateUI.java +++ b/src/main/java/org/opendatakit/aggregate/client/AggregateUI.java @@ -307,6 +307,7 @@ public void updateOdkTablesFeatureVisibility() { if ( w != null && w instanceof OdkTablesTabUI ) { w.setVisible(odkTablesVisible); ((Widget) mainNav.getTabBar().getTab(i)).setVisible(odkTablesVisible); + ((OdkTablesTabUI) w).updateVisibilityOdkTablesAdminSubTabs(); } } diff --git a/src/main/java/org/opendatakit/aggregate/client/OdkTablesManageAppLevelFilesSubTab.java b/src/main/java/org/opendatakit/aggregate/client/OdkTablesManageAppLevelFilesSubTab.java new file mode 100644 index 0000000000..796fd6e1e9 --- /dev/null +++ b/src/main/java/org/opendatakit/aggregate/client/OdkTablesManageAppLevelFilesSubTab.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2013 University of Washington + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.opendatakit.aggregate.client; + +import org.opendatakit.aggregate.client.table.OdkTablesViewAppLevelFileInfo; +import org.opendatakit.aggregate.client.widgets.ServletPopupButton; +import org.opendatakit.aggregate.constants.common.UIConsts; +import org.opendatakit.common.security.common.GrantedAuthorityName; + +import com.google.gwt.user.client.ui.FlexTable; +import com.google.gwt.user.client.ui.HasHorizontalAlignment; +import com.google.gwt.user.client.ui.HorizontalPanel; + +/** + * This class builds the subtab that allows for viewing and managing the files + * that are associated with an appName but not an individual tableId.
+ * + * Copied from ODKTableManageTableFilesSubTab + * + * @author sudar.sam@gmail.com + * + */ +public class OdkTablesManageAppLevelFilesSubTab extends AggregateSubTabBase { + + // this is the panel with the add button + private FlexTable selectTablePanel; + + private HorizontalPanel topPanel; + + // the string constants for adding a file + private static final String ADD_FILE_TXT = "Add an application file"; + private static final String ADD_FILE_TOOLTIP_TXT = "Upload a file"; + private static final String ADD_FILE_BALLOON_TXT = "Upload a file that is not associated with a specific tableId"; + private static final String ADD_FILE_BUTTON_TXT = "" + + ADD_FILE_TXT; + + // this is a button for adding a file. + private ServletPopupButton addFileButton; + + // the box that shows the data + private OdkTablesViewAppLevelFileInfo tableFileData; + + /** + * Sets up the View Table subtab. + */ + public OdkTablesManageAppLevelFilesSubTab() { + + addFileButton = new ServletPopupButton(ADD_FILE_BUTTON_TXT, ADD_FILE_TXT, + UIConsts.APP_LEVEL_FILE_UPLOAD_SERVLET_ADDR, this, ADD_FILE_TOOLTIP_TXT, ADD_FILE_BALLOON_TXT); + + setStylePrimaryName(UIConsts.VERTICAL_FLOW_PANEL_STYLENAME); + + tableFileData = new OdkTablesViewAppLevelFileInfo(this); + + selectTablePanel = new FlexTable(); + selectTablePanel.getElement().setId("app_level_panel"); + selectTablePanel.setHTML(0, 0, "

Application Level Files

"); + selectTablePanel.setWidget(1, 0, addFileButton); + + topPanel = new HorizontalPanel(); + topPanel.add(selectTablePanel); + topPanel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_JUSTIFY); + add(topPanel); + add(tableFileData); + } + + @Override + public boolean canLeave() { + // sure you can leave. + return true; + } + + @Override + public void update() { + + if ( AggregateUI.getUI().getUserInfo().getGrantedAuthorities().contains(GrantedAuthorityName.ROLE_ADMINISTER_TABLES)) { + tableFileData.updateData(); + } + } + +} diff --git a/src/main/java/org/opendatakit/aggregate/client/OdkTablesManageInstanceFilesSubTab.java b/src/main/java/org/opendatakit/aggregate/client/OdkTablesManageInstanceFilesSubTab.java new file mode 100644 index 0000000000..91f252ecc6 --- /dev/null +++ b/src/main/java/org/opendatakit/aggregate/client/OdkTablesManageInstanceFilesSubTab.java @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2013 University of Washington + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.opendatakit.aggregate.client; + +import java.util.ArrayList; + +import org.opendatakit.aggregate.client.odktables.TableEntryClient; +import org.opendatakit.aggregate.client.table.OdkTablesViewInstanceFileInfo; +import org.opendatakit.aggregate.client.widgets.OdkTablesTableIdServletPopupButton; +import org.opendatakit.aggregate.client.widgets.OdkTablesTableIdServletPopupButton.OdkTablesData; +import org.opendatakit.aggregate.constants.common.UIConsts; +import org.opendatakit.common.security.client.exception.AccessDeniedException; +import org.opendatakit.common.security.common.GrantedAuthorityName; + +import com.google.gwt.event.dom.client.ChangeEvent; +import com.google.gwt.event.dom.client.ChangeHandler; +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.google.gwt.user.client.ui.FlexTable; +import com.google.gwt.user.client.ui.HasHorizontalAlignment; +import com.google.gwt.user.client.ui.HorizontalPanel; +import com.google.gwt.user.client.ui.ListBox; + +/** + * This class builds the subtab that allows for viewing and managing the files + * that are associated with data rows in an ODKTables tables.
+ * + * @author sudar.sam@gmail.com + * + */ +public class OdkTablesManageInstanceFilesSubTab extends AggregateSubTabBase + implements OdkTablesData { + + // this is the panel with the information and the dropdown box + // that tells you to select a table + private FlexTable selectTablePanel; + + // the string constants for adding a file + private static final String ADD_FILE_TXT = "Add an instance (data row) file"; + private static final String ADD_FILE_TOOLTIP_TXT = "Upload a file"; + private static final String ADD_FILE_BALLOON_TXT = "Upload a file to be associated with a specific data row of a table"; + private static final String ADD_FILE_BUTTON_TXT = "" + + ADD_FILE_TXT; + + // this is a button for adding a file to be associated with a table. + private OdkTablesTableIdServletPopupButton addFileButton; + /** + * This will be the box that lets you choose which of the tables you are going + * to view. + * + * @return + */ + private ListBox tableBox; + /** + * This is the int in the list box that is selected. + */ + private int selectedValue; + + private HorizontalPanel topPanel; + + // array list so that you can access with indices reliably + private final ArrayList currentTables; + // the box that shows the data + private OdkTablesViewInstanceFileInfo tableFileData; + + // the current table that is being displayed + private TableEntryClient currentTable; + + /** + * Sets up the View Table subtab. + */ + public OdkTablesManageInstanceFilesSubTab() { + + addFileButton = new OdkTablesTableIdServletPopupButton(ADD_FILE_BUTTON_TXT, ADD_FILE_TXT, + UIConsts.TABLE_FILE_UPLOAD_SERVLET_ADDR, ADD_FILE_TOOLTIP_TXT, ADD_FILE_BALLOON_TXT, + this, this); + + setStylePrimaryName(UIConsts.VERTICAL_FLOW_PANEL_STYLENAME); + + // displayDeleted = false; + currentTable = null; + + // first construct a copy so you can build the list box before you + // update it. This seems like bad style. + currentTables = new ArrayList(); + + // set up the box so you can only select one and you provide both the + // table name and ID. + tableBox = new ListBox(); + // new TableEntryClientListBox(currentTables, false, false, + // "Select a table to view."); + tableBox.addChangeHandler(new ChangeHandler() { + + public void onChange(ChangeEvent event) { + int selectedIndex = tableBox.getSelectedIndex(); + // Call this to clear the contents while you are waiting on + // the response from the server. + tableFileData.updateDisplay(null); + currentTable = null; + selectedValue = selectedIndex; + updateContentsForSelectedTable(); + } + }); + + tableFileData = new OdkTablesViewInstanceFileInfo(this); + + selectTablePanel = new FlexTable(); + selectTablePanel.getElement().setId("select_table_panel"); + selectTablePanel.setHTML(0, 0, "

Select a Table

"); + selectTablePanel.setWidget(0, 1, tableBox); + selectTablePanel.setWidget(1, 0, addFileButton); + + // deletedRowsCheckBox = new OdkTablesDisplayDeletedRowsCheckBox(this, + // displayDeleted); + // selectTablePanel.setWidget(0, 2, deletedRowsCheckBox); + + topPanel = new HorizontalPanel(); + topPanel.add(selectTablePanel); + topPanel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_JUSTIFY); + add(topPanel); + add(tableFileData); + + } + + /** + * Call this to remove any currently displayed data, set the selected table in + * the list box to zero, and generally reset this page. + */ + public void setTabToDislpayZero() { + selectedValue = 0; + tableBox.setSelectedIndex(0); + updateContentsForSelectedTable(); + } + + private void updateTableList() { + SecureGWT.getServerTableService().getTables(new AsyncCallback>() { + + @Override + public void onFailure(Throwable caught) { + if ( caught instanceof AccessDeniedException ) { + // swallow it... + AggregateUI.getUI().clearError(); + ArrayList tables = new ArrayList(); + addTablesToListBox(tables); + tableBox.clear(); + setTabToDislpayZero(); + } else { + AggregateUI.getUI().reportError(caught); + } + } + + @Override + public void onSuccess(ArrayList tables) { + AggregateUI.getUI().clearError(); + + addTablesToListBox(tables); + tableBox.setItemSelected(selectedValue, true); + + // This makes the server go crazy with requests. + // AggregateUI.getUI().getTimer().refreshNow(); + + } + }); + } + + @Override + public boolean canLeave() { + // sure you can leave. + return true; + } + + /* + * temporarily existed to display deleted rows public Boolean + * getDisplayDeleted() { return displayDeleted; } + * + * public void setDisplayDeleted(Boolean display) { this.displayDeleted = + * display; } + */ + + /** + * This should just update the table list. + */ + // does so by calling other methods. + @Override + public void update() { + + if ( AggregateUI.getUI().getUserInfo().getGrantedAuthorities().contains(GrantedAuthorityName.ROLE_SYNCHRONIZE_TABLES)) { + updateTableList(); + // this causing trouble + updateTableData(); + } + } + + public void addTablesToListBox(ArrayList tables) { + // clear the old tables + currentTables.clear(); + // and add the new + currentTables.addAll(tables); + + // now update the list box + tableBox.clear(); + tableBox.addItem(""); // blank holder to start with no selection + for (int i = 0; i < currentTables.size(); i++) { + tableBox.addItem(currentTables.get(i).getDisplayName()); + } + } + + public void updateContentsForSelectedTable() { + // - 1 because you have an extra entry that is the "" holder so + // that the listbox starts empty. + if (this.selectedValue == 0) { + // if they select 0, clear the table + tableFileData.updateDisplay(null); + // we also want to have no curren table. + currentTable = null; + // clear the "displaying" thing + if ( selectTablePanel.getRowCount() > 2 ) { + selectTablePanel.removeRow(2); + } + } else { + currentTable = currentTables.get(this.selectedValue - 1); + tableFileData.updateDisplay(currentTable); + + selectTablePanel.setHTML(2, 0, "

Displaying:

"); + selectTablePanel.setHTML(2, 1, "

" + currentTable.getDisplayName() + + "

"); + add(tableFileData); + } + } + + @Override + public String getTableId() { + if ( currentTable == null ) { + return null; + } else { + return currentTable.getTableId(); + } + } + + public void updateTableData() { + tableFileData.updateDisplay(currentTable); + } + + /** + * Set the table to be displayed. You have to set the it in the selectedValue + * and update it. O(n), could be improved. + * + * @param tableId + */ + public void setCurrentTable(String tableId) { + boolean foundTable = false; + // We want to traverse the list of tables and find the index. + for (int i = 0; i < currentTables.size(); i++) { + if (currentTables.get(i).getTableId().equals(tableId)) { + // +1 because the zeroth spot in the list box is the blank placeholder + selectedValue = i + 1; + currentTable = currentTables.get(i); + updateContentsForSelectedTable(); + foundTable = true; + break; + } + } + if (!foundTable) { + selectedValue = 0; + tableFileData.removeAllRows(); + } + } + +} diff --git a/src/main/java/org/opendatakit/aggregate/client/OdkTablesManageTableFilesSubTab.java b/src/main/java/org/opendatakit/aggregate/client/OdkTablesManageTableFilesSubTab.java index 752dab2996..ba0e7e94ee 100644 --- a/src/main/java/org/opendatakit/aggregate/client/OdkTablesManageTableFilesSubTab.java +++ b/src/main/java/org/opendatakit/aggregate/client/OdkTablesManageTableFilesSubTab.java @@ -20,9 +20,11 @@ import org.opendatakit.aggregate.client.odktables.TableEntryClient; import org.opendatakit.aggregate.client.table.OdkTablesViewTableFileInfo; -import org.opendatakit.aggregate.client.widgets.ServletPopupButton; +import org.opendatakit.aggregate.client.widgets.OdkTablesTableIdServletPopupButton; +import org.opendatakit.aggregate.client.widgets.OdkTablesTableIdServletPopupButton.OdkTablesData; import org.opendatakit.aggregate.constants.common.UIConsts; import org.opendatakit.common.security.client.exception.AccessDeniedException; +import org.opendatakit.common.security.common.GrantedAuthorityName; import com.google.gwt.event.dom.client.ChangeEvent; import com.google.gwt.event.dom.client.ChangeHandler; @@ -44,7 +46,7 @@ * @author sudar.sam@gmail.com * */ -public class OdkTablesManageTableFilesSubTab extends AggregateSubTabBase { +public class OdkTablesManageTableFilesSubTab extends AggregateSubTabBase implements OdkTablesData { // this is the panel with the information and the dropdown box // that tells you to select a table @@ -58,7 +60,7 @@ public class OdkTablesManageTableFilesSubTab extends AggregateSubTabBase { + ADD_FILE_TXT; // this is a button for adding a file to be associated with a table. - private ServletPopupButton addFileButton; + private OdkTablesTableIdServletPopupButton addFileButton; /** * This will be the box that lets you choose which of the tables you are going * to view. @@ -86,8 +88,8 @@ public class OdkTablesManageTableFilesSubTab extends AggregateSubTabBase { */ public OdkTablesManageTableFilesSubTab() { - addFileButton = new ServletPopupButton(ADD_FILE_BUTTON_TXT, ADD_FILE_TXT, - UIConsts.TABLE_FILE_UPLOAD_SERVLET_ADDR, this, ADD_FILE_TOOLTIP_TXT, ADD_FILE_BALLOON_TXT); + addFileButton = new OdkTablesTableIdServletPopupButton(ADD_FILE_BUTTON_TXT, ADD_FILE_TXT, + UIConsts.TABLE_FILE_UPLOAD_SERVLET_ADDR, ADD_FILE_TOOLTIP_TXT, ADD_FILE_BALLOON_TXT, this, this); setStylePrimaryName(UIConsts.VERTICAL_FLOW_PANEL_STYLENAME); @@ -116,9 +118,6 @@ public void onChange(ChangeEvent event) { } }); - // now populate the list. - updateTableList(); - tableFileData = new OdkTablesViewTableFileInfo(this); selectTablePanel = new FlexTable(); @@ -200,9 +199,12 @@ public boolean canLeave() { // does so by calling other methods. @Override public void update() { - updateTableList(); - // this causing trouble - updateTableData(); + + if ( AggregateUI.getUI().getUserInfo().getGrantedAuthorities().contains(GrantedAuthorityName.ROLE_ADMINISTER_TABLES)) { + updateTableList(); + // this causing trouble + updateTableData(); + } } public void addTablesToListBox(ArrayList tables) { @@ -242,6 +244,15 @@ public void updateContentsForSelectedTable() { } } + @Override + public String getTableId() { + if ( currentTable == null ) { + return null; + } else { + return currentTable.getTableId(); + } + } + public void updateTableData() { tableFileData.updateDisplay(currentTable); } diff --git a/src/main/java/org/opendatakit/aggregate/client/OdkTablesTabUI.java b/src/main/java/org/opendatakit/aggregate/client/OdkTablesTabUI.java index 9a8affe2bd..51f2656057 100644 --- a/src/main/java/org/opendatakit/aggregate/client/OdkTablesTabUI.java +++ b/src/main/java/org/opendatakit/aggregate/client/OdkTablesTabUI.java @@ -18,18 +18,67 @@ import org.opendatakit.aggregate.constants.common.SubTabs; import org.opendatakit.aggregate.constants.common.Tabs; +import org.opendatakit.common.security.common.GrantedAuthorityName; + +import com.google.gwt.user.client.ui.Widget; public class OdkTablesTabUI extends AggregateTabBase { - public OdkTablesTabUI(AggregateUI baseUI) { - super(); + public OdkTablesTabUI(AggregateUI baseUI) { + super(); + + // add the subtabs + addSubTab(new OdkTablesCurrentTablesSubTab(), SubTabs.CURRENTTABLES); + addSubTab(new OdkTablesViewTableSubTab(), SubTabs.VIEWTABLE); + addSubTab(new OdkTablesManageInstanceFilesSubTab(), SubTabs.MANAGE_INSTANCE_FILES); + addSubTab(new OdkTablesManageTableFilesSubTab(), SubTabs.MANAGE_TABLE_ID_FILES); + addSubTab(new OdkTablesManageAppLevelFilesSubTab(), SubTabs.MANAGE_APP_LEVEL_FILES); + + updateVisibilityOdkTablesAdminSubTabs(); + + // register handler to manage tab selection change (and selecting our tab) + registerClickHandlers(Tabs.ODKTABLES, baseUI); + } + + public void updateVisibilityOdkTablesAdminSubTabs() { + + if (AggregateUI.getUI().getUserInfo().getGrantedAuthorities() + .contains(GrantedAuthorityName.ROLE_ADMINISTER_TABLES)) { + changeVisibilityOdkTablesAdminSubTabs(true); + } else { + changeVisibilityOdkTablesAdminSubTabs(false); + } + + } + + private void changeVisibilityOdkTablesAdminSubTabs(boolean outcome) { + + // hide the app-level files sub-tab + { + SubTabInterface odkTablesAdmin = getSubTab(SubTabs.MANAGE_APP_LEVEL_FILES); + OdkTablesManageAppLevelFilesSubTab subTab = ((OdkTablesManageAppLevelFilesSubTab) odkTablesAdmin); + if (subTab != null) { + subTab.setVisible(outcome); + } + } + // hide the app-level files sub-tab + { + SubTabInterface odkTablesAdmin = getSubTab(SubTabs.MANAGE_TABLE_ID_FILES); + OdkTablesManageTableFilesSubTab subTab = ((OdkTablesManageTableFilesSubTab) odkTablesAdmin); + if (subTab != null) { + subTab.setVisible(outcome); + } + } - // add the subtabs - addSubTab(new OdkTablesCurrentTablesSubTab(), SubTabs.CURRENTTABLES); - addSubTab(new OdkTablesViewTableSubTab(), SubTabs.VIEWTABLE); - addSubTab(new OdkTablesManageTableFilesSubTab(), SubTabs.MANAGEFILES); + for (int i = 0; i < subTabPosition.size(); i++) { + if (subTabPosition.get(i).equals(SubTabs.MANAGE_APP_LEVEL_FILES) || + subTabPosition.get(i).equals(SubTabs.MANAGE_TABLE_ID_FILES)) { + Widget w = ((Widget) this.getTabBar().getTab(i)); + if (w != null) { + w.setVisible(outcome); + } + } + } + } - // register handler to manage tab selection change (and selecting our tab) - registerClickHandlers(Tabs.ODKTABLES, baseUI); - } } diff --git a/src/main/java/org/opendatakit/aggregate/client/RefreshTimer.java b/src/main/java/org/opendatakit/aggregate/client/RefreshTimer.java index b11cf9b968..7295e01a6a 100644 --- a/src/main/java/org/opendatakit/aggregate/client/RefreshTimer.java +++ b/src/main/java/org/opendatakit/aggregate/client/RefreshTimer.java @@ -228,7 +228,7 @@ public void run() { tabPanel.update(); } break; - case MANAGEFILES: + case MANAGE_TABLE_ID_FILES: if ((intervalsCount % MISC_REFRESH_MULTIPLIER) == 0) { GWT.log("manage files refresh"); tabPanel.update(); diff --git a/src/main/java/org/opendatakit/aggregate/client/odktables/ServerDataService.java b/src/main/java/org/opendatakit/aggregate/client/odktables/ServerDataService.java index a66225c334..5a9cf3e17b 100644 --- a/src/main/java/org/opendatakit/aggregate/client/odktables/ServerDataService.java +++ b/src/main/java/org/opendatakit/aggregate/client/odktables/ServerDataService.java @@ -75,7 +75,15 @@ TableContentsClient getTableContents(String tableId) throws AccessDeniedExceptio RequestFailureException, DatastoreFailureException, PermissionDeniedExceptionClient, EntityNotFoundExceptionClient, BadColumnNameExceptionClient; - TableContentsForFilesClient getFileInfoContents(String tableId) throws AccessDeniedException, + TableContentsForFilesClient getAppLevelFileInfoContents() throws AccessDeniedException, + RequestFailureException, DatastoreFailureException, PermissionDeniedExceptionClient, + EntityNotFoundExceptionClient; + + TableContentsForFilesClient getTableFileInfoContents(String tableId) throws AccessDeniedException, + RequestFailureException, DatastoreFailureException, PermissionDeniedExceptionClient, + EntityNotFoundExceptionClient; + + TableContentsForFilesClient getInstanceFileInfoContents(String tableId) throws AccessDeniedException, RequestFailureException, DatastoreFailureException, PermissionDeniedExceptionClient, EntityNotFoundExceptionClient; diff --git a/src/main/java/org/opendatakit/aggregate/client/odktables/ServerDataServiceAsync.java b/src/main/java/org/opendatakit/aggregate/client/odktables/ServerDataServiceAsync.java index cbc6a13aa9..c26e41103e 100644 --- a/src/main/java/org/opendatakit/aggregate/client/odktables/ServerDataServiceAsync.java +++ b/src/main/java/org/opendatakit/aggregate/client/odktables/ServerDataServiceAsync.java @@ -45,7 +45,11 @@ void createOrUpdateRow(String tableId, String rowId, RowClient row, void getTableContents(String tableId, AsyncCallback callback); - void getFileInfoContents(String tableId, AsyncCallback callback); + void getAppLevelFileInfoContents(AsyncCallback callback); + + void getTableFileInfoContents(String tableId, AsyncCallback callback); + + void getInstanceFileInfoContents(String tableId, AsyncCallback callback); void deleteTableFile(String tableId, String rowId, AsyncCallback callback); diff --git a/src/main/java/org/opendatakit/aggregate/client/odktables/TableContentsForFilesClient.java b/src/main/java/org/opendatakit/aggregate/client/odktables/TableContentsForFilesClient.java index 02eae9cd86..d661410e62 100644 --- a/src/main/java/org/opendatakit/aggregate/client/odktables/TableContentsForFilesClient.java +++ b/src/main/java/org/opendatakit/aggregate/client/odktables/TableContentsForFilesClient.java @@ -37,21 +37,16 @@ public class TableContentsForFilesClient implements Serializable { /** * */ - private static final long serialVersionUID = -564495395828330963L; + private static final long serialVersionUID = -564495395828330964L; public TableContentsForFilesClient() { // necessary for gwt serialization } /** - * The names of the table's columns. + * The files for the table. The usage determines whether + * these are table-level or instance files. */ - public ArrayList columnNames; - - /** - * The non-media files for the table. Any media file associated with the file - * will exist in the mediaFiles list for that file. - */ - public ArrayList nonMediaFiles; + public ArrayList files; } diff --git a/src/main/java/org/opendatakit/aggregate/client/odktables/TableResourceClient.java b/src/main/java/org/opendatakit/aggregate/client/odktables/TableResourceClient.java index 6043038a68..d8a630c8eb 100644 --- a/src/main/java/org/opendatakit/aggregate/client/odktables/TableResourceClient.java +++ b/src/main/java/org/opendatakit/aggregate/client/odktables/TableResourceClient.java @@ -37,6 +37,7 @@ public class TableResourceClient extends TableEntryClient implements Serializabl private String definitionUri; private String propertiesUri; private String dataUri; + private String instanceFilesUri; private String diffUri; private String aclUri; @@ -66,6 +67,10 @@ public String getDataUri() { return this.dataUri; } + public String getInstanceFilesUri() { + return this.instanceFilesUri; + } + public String getDiffUri() { return this.diffUri; } @@ -90,6 +95,10 @@ public void setDataUri(final String dataUri) { this.dataUri = dataUri; } + public void setInstanceFilesUri(final String instanceFilesUri) { + this.instanceFilesUri = instanceFilesUri; + } + public void setDiffUri(final String diffUri) { this.diffUri = diffUri; } @@ -122,6 +131,11 @@ public boolean equals(Object obj) { return false; } else if (!dataUri.equals(other.dataUri)) return false; + if (instanceFilesUri == null) { + if (other.instanceFilesUri != null) + return false; + } else if (!instanceFilesUri.equals(other.instanceFilesUri)) + return false; if (diffUri == null) { if (other.diffUri != null) return false; @@ -155,6 +169,7 @@ public int hashCode() { int result = super.hashCode(); result = prime * result + ((aclUri == null) ? 0 : aclUri.hashCode()); result = prime * result + ((dataUri == null) ? 0 : dataUri.hashCode()); + result = prime * result + ((instanceFilesUri == null) ? 0 : instanceFilesUri.hashCode()); result = prime * result + ((diffUri == null) ? 0 : diffUri.hashCode()); result = prime * result + ((propertiesUri == null) ? 0 : propertiesUri.hashCode()); result = prime * result + ((selfUri == null) ? 0 : selfUri.hashCode()); @@ -175,6 +190,8 @@ public String toString() { builder.append(propertiesUri); builder.append(", dataUri="); builder.append(dataUri); + builder.append(", instanceFilesUri="); + builder.append(instanceFilesUri); builder.append(", diffUri="); builder.append(diffUri); builder.append(", aclUri="); diff --git a/src/main/java/org/opendatakit/aggregate/client/table/OdkTablesTableList.java b/src/main/java/org/opendatakit/aggregate/client/table/OdkTablesTableList.java index a0c82739cb..8680a609c6 100644 --- a/src/main/java/org/opendatakit/aggregate/client/table/OdkTablesTableList.java +++ b/src/main/java/org/opendatakit/aggregate/client/table/OdkTablesTableList.java @@ -46,10 +46,10 @@ public class OdkTablesTableList extends FlexTable { private static final String DELETE_BUTTON_HEADING = "Delete"; private static final int VIEW_TABLE_BUTTON_COLUMN = 2; - private static final String VIEW_TABLE_BUTTON_HEADING = ""; + private static final String VIEW_TABLE_BUTTON_HEADING = "View Data"; private static final int TABLE_FILES_BUTTON_COLUMN = 3; - private static final String TABLE_FILE_BUTTON_HEADING = ""; + private static final String TABLE_FILE_BUTTON_HEADING = "View Data Files"; public OdkTablesTableList() { diff --git a/src/main/java/org/opendatakit/aggregate/client/table/OdkTablesViewAppLevelFileInfo.java b/src/main/java/org/opendatakit/aggregate/client/table/OdkTablesViewAppLevelFileInfo.java new file mode 100644 index 0000000000..ccecf5578b --- /dev/null +++ b/src/main/java/org/opendatakit/aggregate/client/table/OdkTablesViewAppLevelFileInfo.java @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2013 University of Washington + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.opendatakit.aggregate.client.table; + +import java.util.ArrayList; + +import org.opendatakit.aggregate.client.AggregateSubTabBase; +import org.opendatakit.aggregate.client.AggregateUI; +import org.opendatakit.aggregate.client.SecureGWT; +import org.opendatakit.aggregate.client.odktables.FileSummaryClient; +import org.opendatakit.aggregate.client.odktables.TableContentsForFilesClient; +import org.opendatakit.aggregate.client.widgets.OdkTablesDeleteFileButton; + +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.google.gwt.user.client.ui.Anchor; +import com.google.gwt.user.client.ui.FlexTable; +import com.google.gwt.user.client.ui.HTML; +import com.google.gwt.user.client.ui.Widget; + +/** + * Displays the entries in the DbTableFileInfo table that pertain to + * the application and not to any specific table. + * + * Based upon OdkTablesViewTableFileInfo + * + * @author sudar.sam@gmail.com + * + */ +public class OdkTablesViewAppLevelFileInfo extends FlexTable { + + // the table whose info we are currently displaying. +// private TableEntryClient currentTable; + + // that table's info rows + private ArrayList fileSummaries; + + private static final int DELETE_COLUMN = 0; + // this is the heading for the delete row button. + private static final String DELETE_HEADING = "Delete"; + + private static final int ODK_CLIENT_VERSION_COLUMN = 1; + private static final String ODK_CLIENT_VERSION_HEADING = "Client Version"; + private static final int FILENAME_COLUMN = 2; + private static final String FILENAME_HEADING = "Filename"; + private static final int DOWNLOAD_COLUMN = 3; + private static final String DOWNLOAD_HEADING = "Download"; + + private static final int numColumns = 4; + + // this is just the tab that opened the table + private AggregateSubTabBase basePanel; + + // the message to display when there are no rows in the table + private static String NO_ROWS_MESSAGE = "There are no rows to display."; + + public OdkTablesViewAppLevelFileInfo(AggregateSubTabBase tableSubTab) { + + setColumnHeadings(); + + // add styling + addStyleName("dataTable"); + getElement().setId("form_management_table"); + + this.basePanel = tableSubTab; + } + + public void updateData() { + // set up the callback object + AsyncCallback getDataCallback = new AsyncCallback() { + @Override + public void onFailure(Throwable caught) { + AggregateUI.getUI().reportError(caught); + } + + @Override + public void onSuccess(TableContentsForFilesClient tcc) { + removeAllRows(); + setColumnHeadings(); + fileSummaries = tcc.files; + setRows(); + } + }; + + SecureGWT.getServerDataService().getAppLevelFileInfoContents(getDataCallback); + } + + private void setColumnHeadings() { + // create the table headers. + setText(0, DELETE_COLUMN, DELETE_HEADING); + setText(0, ODK_CLIENT_VERSION_COLUMN, ODK_CLIENT_VERSION_HEADING); + setText(0, FILENAME_COLUMN, FILENAME_HEADING); + setText(0, DOWNLOAD_COLUMN, DOWNLOAD_HEADING); + getRowFormatter().addStyleName(0, "titleBar"); + } + + /* + * This will set the row values in the listbox. + */ + private void setRows() { + int start = 1; // b/c the 0 row is the headings. + + int currentRow = start; + // Window.alert(Integer.toString(rows.size())); + // if there are no columns, then we only want to display the no data + // message. + // otherwise check if there are no rows. + if (fileSummaries.size() == 0) { + setWidget(currentRow, 0, new HTML(NO_ROWS_MESSAGE)); + // make the display fill all the columns you have. this is the total + // number of + // user-defined columns +1 for the delete column. + this.getFlexCellFormatter().setColSpan(1, 0, numColumns); + } else { // there are rows--display them. + + for (int j = 0; j < fileSummaries.size(); j++) { + FileSummaryClient sum = fileSummaries.get(j); + setWidget(currentRow, DELETE_COLUMN, new OdkTablesDeleteFileButton(this.basePanel, + "", sum.getId())); + setText(currentRow, ODK_CLIENT_VERSION_COLUMN, sum.getOdkClientVersion()); + setText(currentRow, FILENAME_COLUMN, sum.getFilename()); + Widget downloadCol; + if (sum.getDownloadUrl() != null) { + Anchor downloadLink = new Anchor(); + downloadLink.setText("Get"); + downloadLink.setHref(sum.getDownloadUrl()); + downloadCol = downloadLink; + } else { + downloadCol = new HTML(""); + } + setWidget(currentRow, DOWNLOAD_COLUMN, downloadCol); + if (currentRow % 2 == 0) { + getRowFormatter().addStyleName(currentRow, "evenTableRow"); + } + currentRow++; + } + while (getRowCount() > currentRow) { + removeRow(getRowCount() - 1); + } + } + } + +} diff --git a/src/main/java/org/opendatakit/aggregate/client/table/OdkTablesViewInstanceFileInfo.java b/src/main/java/org/opendatakit/aggregate/client/table/OdkTablesViewInstanceFileInfo.java new file mode 100644 index 0000000000..320f3c51ab --- /dev/null +++ b/src/main/java/org/opendatakit/aggregate/client/table/OdkTablesViewInstanceFileInfo.java @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2013 University of Washington + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.opendatakit.aggregate.client.table; + +import java.util.ArrayList; + +import org.opendatakit.aggregate.client.AggregateSubTabBase; +import org.opendatakit.aggregate.client.AggregateUI; +import org.opendatakit.aggregate.client.OdkTablesManageTableFilesSubTab; +import org.opendatakit.aggregate.client.SecureGWT; +import org.opendatakit.aggregate.client.exception.EntityNotFoundExceptionClient; +import org.opendatakit.aggregate.client.odktables.FileSummaryClient; +import org.opendatakit.aggregate.client.odktables.TableContentsForFilesClient; +import org.opendatakit.aggregate.client.odktables.TableEntryClient; +import org.opendatakit.aggregate.client.widgets.OdkTablesDeleteFileButton; +import org.opendatakit.aggregate.constants.common.SubTabs; + +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.google.gwt.user.client.ui.Anchor; +import com.google.gwt.user.client.ui.FlexTable; +import com.google.gwt.user.client.ui.HTML; +import com.google.gwt.user.client.ui.Widget; + +/** + * Displays the files associated with individual rows in a table. + * + * @author sudar.sam@gmail.com + * + */ +public class OdkTablesViewInstanceFileInfo extends FlexTable { + + // the table whose info we are currently displaying. + private TableEntryClient currentTable; + + // that table's info rows + private ArrayList fileSummaries; + + private static final int DELETE_COLUMN = 0; + // this is the heading for the delete row button. + private static final String DELETE_HEADING = "Delete"; + + private static final int KEY_COLUMN = 1; + private static final String KEY_HEADING = "Key"; + private static final int TABLE_ID_COLUMN = 2; + private static final String TABLE_ID_HEADING = "TableId"; + private static final int FILENAME_COLUMN = 3; + private static final String FILENAME_HEADING = "Filename"; + private static final int DOWNLOAD_COLUMN = 4; + private static final String DOWNLOAD_HEADING = "Download"; + + private static final int numColumns = 5; + + // this is just the tab that opened the table + private AggregateSubTabBase basePanel; + + // the message to display when there are no rows in the table + private static String NO_ROWS_MESSAGE = "There are no rows to display."; + + /** + * This is the constructor to call when there has not been a table selected. + * Should this even exist? + */ + public OdkTablesViewInstanceFileInfo(AggregateSubTabBase tableSubTab) { + + setColumnHeadings(); + + // add styling + addStyleName("dataTable"); + getElement().setId("form_management_table"); + + this.basePanel = tableSubTab; + + // no current table. + this.currentTable = null; + } + + public OdkTablesViewInstanceFileInfo(AggregateSubTabBase tableSubTab, TableEntryClient table) { + this(tableSubTab); + + updateDisplay(table); + + this.currentTable = table; + } + + /** + * This updates the display to show the contents of the table. + */ + public void updateDisplay(TableEntryClient table) { + @SuppressWarnings("unused") + TableEntryClient oldTable = this.currentTable; + + this.currentTable = table; + + if (table == null) { + this.removeAllRows(); + } else { + + /*** update the data ***/ + updateData(table); + } + + } + + public void updateData(TableEntryClient table) { + // set up the callback object + AsyncCallback getDataCallback = new AsyncCallback() { + @Override + public void onFailure(Throwable caught) { + if (caught instanceof EntityNotFoundExceptionClient) { + // if this happens it is PROBABLY, but not necessarily, because + // we've deleted the table. + // TODO ensure the correct exception makes it here + ((OdkTablesManageTableFilesSubTab) AggregateUI.getUI().getSubTab(SubTabs.MANAGE_TABLE_ID_FILES)) + .setTabToDislpayZero(); + } else { + AggregateUI.getUI().reportError(caught); + } + } + + @Override + public void onSuccess(TableContentsForFilesClient tcc) { + removeAllRows(); + setColumnHeadings(); + // setColumnHeadings(columnNames); + + fileSummaries = tcc.files; + setRows(); + + // AggregateUI.getUI().getTimer().refreshNow(); + } + }; + + SecureGWT.getServerDataService().getInstanceFileInfoContents(table.getTableId(), getDataCallback); + } + + private void setColumnHeadings() { + // create the table headers. + setText(0, DELETE_COLUMN, DELETE_HEADING); + setText(0, KEY_COLUMN, KEY_HEADING); + setText(0, TABLE_ID_COLUMN, TABLE_ID_HEADING); + setText(0, FILENAME_COLUMN, FILENAME_HEADING); + setText(0, DOWNLOAD_COLUMN, DOWNLOAD_HEADING); + getRowFormatter().addStyleName(0, "titleBar"); + } + + /* + * This will set the row values in the listbox. + */ + private void setRows() { + int start = 1; // b/c the 0 row is the headings. + + int currentRow = start; + // Window.alert(Integer.toString(rows.size())); + // if there are no columns, then we only want to display the no data + // message. + // otherwise check if there are no rows. + if (fileSummaries.size() == 0) { + setWidget(currentRow, 0, new HTML(NO_ROWS_MESSAGE)); + // make the display fill all the columns you have. this is the total + // number of + // user-defined columns +1 for the delete column. + this.getFlexCellFormatter().setColSpan(1, 0, numColumns); + } else { // there are rows--display them. + + for (int j = 0; j < fileSummaries.size(); j++) { + FileSummaryClient sum = fileSummaries.get(j); + setWidget(currentRow, DELETE_COLUMN, new OdkTablesDeleteFileButton(this.basePanel, + currentTable.getTableId(), sum.getId())); + setText(currentRow, KEY_COLUMN, sum.getId()); + setText(currentRow, TABLE_ID_COLUMN, sum.getTableId()); + setText(currentRow, FILENAME_COLUMN, sum.getFilename()); + Widget downloadCol; + if (sum.getDownloadUrl() != null) { + Anchor downloadLink = new Anchor(); + downloadLink.setText("Get"); + downloadLink.setHref(sum.getDownloadUrl()); + downloadCol = downloadLink; + } else { + downloadCol = new HTML(""); + } + setWidget(currentRow, DOWNLOAD_COLUMN, downloadCol); + if (currentRow % 2 == 0) { + getRowFormatter().addStyleName(currentRow, "evenTableRow"); + } + currentRow++; + } + while (getRowCount() > currentRow) { + removeRow(getRowCount() - 1); + } + } + } + +} diff --git a/src/main/java/org/opendatakit/aggregate/client/table/OdkTablesViewTableFileInfo.java b/src/main/java/org/opendatakit/aggregate/client/table/OdkTablesViewTableFileInfo.java index 6f61a7bca0..3fbacc27e3 100644 --- a/src/main/java/org/opendatakit/aggregate/client/table/OdkTablesViewTableFileInfo.java +++ b/src/main/java/org/opendatakit/aggregate/client/table/OdkTablesViewTableFileInfo.java @@ -54,18 +54,14 @@ public class OdkTablesViewTableFileInfo extends FlexTable { // this is the heading for the delete row button. private static final String DELETE_HEADING = "Delete"; - private static final int KEY_COLUMN = 1; - private static final String KEY_HEADING = "Key"; - private static final int ODK_CLIENT_VERSION_COLUMN = 2; + private static final int ODK_CLIENT_VERSION_COLUMN = 1; private static final String ODK_CLIENT_VERSION_HEADING = "Client Version"; - private static final int TABLE_ID_COLUMN = 3; - private static final String TABLE_ID_HEADING = "TableId"; - private static final int FILENAME_COLUMN = 4; + private static final int FILENAME_COLUMN = 2; private static final String FILENAME_HEADING = "Filename"; - private static final int DOWNLOAD_COLUMN = 5; + private static final int DOWNLOAD_COLUMN = 3; private static final String DOWNLOAD_HEADING = "Download"; - private static final int numColumns = 6; + private static final int numColumns = 4; // this is just the tab that opened the table private AggregateSubTabBase basePanel; @@ -127,7 +123,7 @@ public void onFailure(Throwable caught) { // if this happens it is PROBABLY, but not necessarily, because // we've deleted the table. // TODO ensure the correct exception makes it here - ((OdkTablesManageTableFilesSubTab) AggregateUI.getUI().getSubTab(SubTabs.MANAGEFILES)) + ((OdkTablesManageTableFilesSubTab) AggregateUI.getUI().getSubTab(SubTabs.MANAGE_TABLE_ID_FILES)) .setTabToDislpayZero(); } else { AggregateUI.getUI().reportError(caught); @@ -140,22 +136,20 @@ public void onSuccess(TableContentsForFilesClient tcc) { setColumnHeadings(); // setColumnHeadings(columnNames); - fileSummaries = tcc.nonMediaFiles; + fileSummaries = tcc.files; setRows(); // AggregateUI.getUI().getTimer().refreshNow(); } }; - SecureGWT.getServerDataService().getFileInfoContents(table.getTableId(), getDataCallback); + SecureGWT.getServerDataService().getTableFileInfoContents(table.getTableId(), getDataCallback); } private void setColumnHeadings() { // create the table headers. setText(0, DELETE_COLUMN, DELETE_HEADING); - setText(0, KEY_COLUMN, KEY_HEADING); setText(0, ODK_CLIENT_VERSION_COLUMN, ODK_CLIENT_VERSION_HEADING); - setText(0, TABLE_ID_COLUMN, TABLE_ID_HEADING); setText(0, FILENAME_COLUMN, FILENAME_HEADING); setText(0, DOWNLOAD_COLUMN, DOWNLOAD_HEADING); getRowFormatter().addStyleName(0, "titleBar"); @@ -184,9 +178,7 @@ private void setRows() { FileSummaryClient sum = fileSummaries.get(j); setWidget(currentRow, DELETE_COLUMN, new OdkTablesDeleteFileButton(this.basePanel, currentTable.getTableId(), sum.getId())); - setText(currentRow, KEY_COLUMN, sum.getId()); setText(currentRow, ODK_CLIENT_VERSION_COLUMN, sum.getOdkClientVersion()); - setText(currentRow, TABLE_ID_COLUMN, sum.getTableId()); setText(currentRow, FILENAME_COLUMN, sum.getFilename()); Widget downloadCol; if (sum.getDownloadUrl() != null) { diff --git a/src/main/java/org/opendatakit/aggregate/client/widgets/OdkTablesShowTableFilesButton.java b/src/main/java/org/opendatakit/aggregate/client/widgets/OdkTablesShowTableFilesButton.java index 919ffe8893..3a3112492f 100644 --- a/src/main/java/org/opendatakit/aggregate/client/widgets/OdkTablesShowTableFilesButton.java +++ b/src/main/java/org/opendatakit/aggregate/client/widgets/OdkTablesShowTableFilesButton.java @@ -17,7 +17,7 @@ package org.opendatakit.aggregate.client.widgets; import org.opendatakit.aggregate.client.AggregateUI; -import org.opendatakit.aggregate.client.OdkTablesManageTableFilesSubTab; +import org.opendatakit.aggregate.client.OdkTablesManageInstanceFilesSubTab; import org.opendatakit.aggregate.client.table.OdkTablesTableList; import org.opendatakit.aggregate.constants.common.SubTabs; @@ -27,9 +27,9 @@ public class OdkTablesShowTableFilesButton extends AggregateButton implements ClickHandler { private static final String BUTTON_TXT = - " Files Associated with Table"; - private static final String TOOLTIP_TXT = "Display Files for this Table"; - private static final String HELP_BALLOON_TXT = "View the files that have " + + " Data Files Associated with Table"; + private static final String TOOLTIP_TXT = "Display Data Files for this Table"; + private static final String HELP_BALLOON_TXT = "View the data files that have " + "been uploaded for this table."; // the table id of the button this table is tied to. @@ -48,11 +48,11 @@ public OdkTablesShowTableFilesButton(OdkTablesTableList parentTable, @Override public void onClick(ClickEvent event) { super.onClick(event); - OdkTablesManageTableFilesSubTab fileSubTab = - (OdkTablesManageTableFilesSubTab) AggregateUI.getUI() - .getSubTab(SubTabs.MANAGEFILES); + OdkTablesManageInstanceFilesSubTab fileSubTab = + (OdkTablesManageInstanceFilesSubTab) AggregateUI.getUI() + .getSubTab(SubTabs.MANAGE_INSTANCE_FILES); fileSubTab.setCurrentTable(tableId); fileSubTab.update(); - AggregateUI.getUI().redirectToSubTab(SubTabs.MANAGEFILES); + AggregateUI.getUI().redirectToSubTab(SubTabs.MANAGE_INSTANCE_FILES); } } diff --git a/src/main/java/org/opendatakit/aggregate/client/widgets/OdkTablesTableIdServletPopupButton.java b/src/main/java/org/opendatakit/aggregate/client/widgets/OdkTablesTableIdServletPopupButton.java new file mode 100644 index 0000000000..d6f8a27815 --- /dev/null +++ b/src/main/java/org/opendatakit/aggregate/client/widgets/OdkTablesTableIdServletPopupButton.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2011 University of Washington + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.opendatakit.aggregate.client.widgets; + +import org.opendatakit.aggregate.client.AggregateSubTabBase; +import org.opendatakit.aggregate.client.popups.ViewServletPopup; + +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.event.logical.shared.CloseEvent; +import com.google.gwt.event.logical.shared.CloseHandler; +import com.google.gwt.user.client.ui.PopupPanel; + +public final class OdkTablesTableIdServletPopupButton extends AggregateButton implements ClickHandler { + + public interface OdkTablesData { + String getTableId(); + }; + + private final String url; + private final String title; + private final AggregateSubTabBase basePanel; + private final OdkTablesData dataAccess; + + public OdkTablesTableIdServletPopupButton(String buttonText, String title, String url, + String tooltipText, String balloonText, AggregateSubTabBase basePanel, OdkTablesData dataAccess) { + super(buttonText, tooltipText, balloonText); + this.title = title; + this.url = url; + this.basePanel = basePanel; + this.dataAccess = dataAccess; + } + + @Override + public void onClick(ClickEvent event) { + super.onClick(event); + + String tableId = dataAccess.getTableId(); + if ( tableId == null ) { + return; + } + ViewServletPopup servletPopup = new ViewServletPopup(title, + url + "?table_id=" + tableId); + servletPopup.setPopupPositionAndShow(servletPopup.getPositionCallBack()); + servletPopup.addCloseHandler(new CloseHandler() { + + @Override + public void onClose(CloseEvent event) { + if(basePanel != null) { + basePanel.update(); + } + } + + }); + } +} \ No newline at end of file diff --git a/src/main/java/org/opendatakit/aggregate/constants/common/SubTabs.java b/src/main/java/org/opendatakit/aggregate/constants/common/SubTabs.java index 0560824247..96d2d4f34e 100644 --- a/src/main/java/org/opendatakit/aggregate/constants/common/SubTabs.java +++ b/src/main/java/org/opendatakit/aggregate/constants/common/SubTabs.java @@ -35,7 +35,9 @@ public enum SubTabs implements Serializable { // These fall under the ODKTables Tab CURRENTTABLES("Current Tables", "viewCurrentTables"), VIEWTABLE("View Table", "viewTable"), - MANAGEFILES("Manage Table Files", "manageTableFiles"); + MANAGE_INSTANCE_FILES("Manage Instance Files", "manageInstanceFiles"), + MANAGE_TABLE_ID_FILES("Manage Table Files", "manageTableFiles"), + MANAGE_APP_LEVEL_FILES("Manage App Level Files", "manageAppLevelFiles"); private String tabLabel; private String hashString; diff --git a/src/main/java/org/opendatakit/aggregate/constants/common/UIConsts.java b/src/main/java/org/opendatakit/aggregate/constants/common/UIConsts.java index 3d7d41bbf6..0e328d6a89 100644 --- a/src/main/java/org/opendatakit/aggregate/constants/common/UIConsts.java +++ b/src/main/java/org/opendatakit/aggregate/constants/common/UIConsts.java @@ -35,6 +35,9 @@ public class UIConsts { public static final String ENKETO_SERVICE_ACCOUNT_PRIVATE_KEY_UPLOAD_ADDR = "ssl/enketo-service-account"; public static final String ENKETO_API_HANDLER_ADDR = "enk/enketoApiHandler"; + // url pattern for uploading files associated with ODKTables tables + public static final String APP_LEVEL_FILE_UPLOAD_SERVLET_ADDR = + "appLevelFileUpload"; // url pattern for uploading files associated with ODKTables tables public static final String TABLE_FILE_UPLOAD_SERVLET_ADDR = "tableFileUpload"; diff --git a/src/main/java/org/opendatakit/aggregate/odktables/FileManifestManager.java b/src/main/java/org/opendatakit/aggregate/odktables/FileManifestManager.java index d441f5297b..353fb77dc1 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/FileManifestManager.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/FileManifestManager.java @@ -132,12 +132,14 @@ private ArrayList getEntriesFromQuery( continue; } entry.filename = pathToFile; + entry.contentLength = blobEntitySet.getContentLength(1, cc); + entry.contentType = blobEntitySet.getContentType(1, cc); entry.md5hash = blobEntitySet.getContentHash(1, cc); String urlPartial = cc.getServerURL() + BasicConsts.FORWARDSLASH + ServletConsts.ODK_TABLES_SERVLET_BASE_PATH + BasicConsts.FORWARDSLASH + appId + BasicConsts.FORWARDSLASH + FileService.SERVLET_PATH + BasicConsts.FORWARDSLASH - + pathToFile; - String urlComplete = HtmlUtil.createLinkWithProperties(urlPartial, properties); + + odkClientVersion + BasicConsts.FORWARDSLASH+ pathToFile; + String urlComplete = HtmlUtil.createLinkWithProperties(urlPartial, properties); entry.downloadUrl = urlComplete; manifestEntries.add(entry); } diff --git a/src/main/java/org/opendatakit/aggregate/odktables/api/InstanceFileService.java b/src/main/java/org/opendatakit/aggregate/odktables/api/InstanceFileService.java index cd8ffe6799..b7bea56c97 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/api/InstanceFileService.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/api/InstanceFileService.java @@ -24,6 +24,7 @@ import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; @@ -31,6 +32,7 @@ import javax.ws.rs.core.Response; import org.jboss.resteasy.annotations.GZIP; +import org.opendatakit.aggregate.odktables.rest.ApiConstants; import org.opendatakit.common.persistence.exception.ODKTaskLockException; /** @@ -64,19 +66,30 @@ public interface InstanceFileService { public static final String SERVLET_PATH = "files"; public static final String PARAM_AS_ATTACHMENT = "as_attachment"; - public static final String PARAM_MD5_HASH_ONLY = "md5_hash_only"; public static final String ERROR_MSG_INSUFFICIENT_PATH = "Not Enough Path Segments: must be at least 2."; public static final String ERROR_MSG_UNRECOGNIZED_APP_ID = "Unrecognized app id: "; public static final String ERROR_MSG_PATH_NOT_UNDER_APP_ID = "File path is not under app id: "; public static final String MIME_TYPE_IMAGE_JPEG = "image/jpeg"; @GET - @Path("{filePath:.*}") + @Path("manifest") + @Produces({MediaType.APPLICATION_JSON, ApiConstants.MEDIA_TEXT_XML_UTF8, ApiConstants.MEDIA_APPLICATION_XML_UTF8}) @GZIP - public Response getFile(@PathParam("filePath") List segments, @QueryParam(PARAM_AS_ATTACHMENT) String asAttachment, @QueryParam(PARAM_MD5_HASH_ONLY) String asMd5HashOnly) throws IOException; + public Response getManifestAll(@QueryParam(PARAM_AS_ATTACHMENT) String asAttachment) throws IOException; + + @GET + @Path("manifest/{filePath:.*}") + @Produces({MediaType.APPLICATION_JSON, ApiConstants.MEDIA_TEXT_XML_UTF8, ApiConstants.MEDIA_APPLICATION_XML_UTF8}) + @GZIP + public Response getManifest(@PathParam("filePath") List segments, @QueryParam(PARAM_AS_ATTACHMENT) String asAttachment) throws IOException; + + @GET + @Path("file/{filePath:.*}") + @GZIP + public Response getFile(@PathParam("filePath") List segments, @QueryParam(PARAM_AS_ATTACHMENT) String asAttachment) throws IOException; @POST - @Path("{filePath:.*}") + @Path("file/{filePath:.*}") @Consumes({MediaType.MEDIA_TYPE_WILDCARD}) public Response putFile(@Context HttpServletRequest req, @PathParam("filePath") List segments, @GZIP byte[] content) throws IOException, ODKTaskLockException; diff --git a/src/main/java/org/opendatakit/aggregate/odktables/api/TableService.java b/src/main/java/org/opendatakit/aggregate/odktables/api/TableService.java index 932c987a19..e0d7e00f2c 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/api/TableService.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/api/TableService.java @@ -139,21 +139,21 @@ public PropertiesService getProperties(@PathParam("appId") String appId, @PathPa * * @param appId * @param tableId - * @return {@link DiffService} for the row-changes on this table. + * @return {@link InstanceFileService} for file attachments to the rows on this table. * @throws ODKDatastoreException */ - @Path("{tableId}/diff") - public DiffService getDiff(@PathParam("appId") String appId, @PathParam("tableId") String tableId) throws ODKDatastoreException; + @Path("{tableId}/attachments") + public InstanceFileService getInstanceFiles(@PathParam("appId") String appId, @PathParam("tableId") String tableId) throws ODKDatastoreException; /** * * @param appId * @param tableId - * @return {@link TableAclService} for ACL management on this table. + * @return {@link DiffService} for the row-changes on this table. * @throws ODKDatastoreException */ - @Path("{tableId}/acl") - public TableAclService getAcl(@PathParam("appId") String appId, @PathParam("tableId") String tableId) throws ODKDatastoreException; + @Path("{tableId}/diff") + public DiffService getDiff(@PathParam("appId") String appId, @PathParam("tableId") String tableId) throws ODKDatastoreException; /** * @@ -162,6 +162,6 @@ public PropertiesService getProperties(@PathParam("appId") String appId, @PathPa * @return {@link TableAclService} for ACL management on this table. * @throws ODKDatastoreException */ - @Path("{tableId}/instances") - public InstanceFileService getInstanceFiles(@PathParam("appId") String appId, @PathParam("tableId") String tableId) throws ODKDatastoreException; + @Path("{tableId}/acl") + public TableAclService getAcl(@PathParam("appId") String appId, @PathParam("tableId") String tableId) throws ODKDatastoreException; } diff --git a/src/main/java/org/opendatakit/aggregate/odktables/entity/UtilTransforms.java b/src/main/java/org/opendatakit/aggregate/odktables/entity/UtilTransforms.java index 2b9e5de93f..c7b47bc54f 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/entity/UtilTransforms.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/entity/UtilTransforms.java @@ -255,6 +255,7 @@ public static TableResourceClient transform(TableResource serverResource, String serverResource.getPropertiesETag(), serverResource.getSchemaETag())); clientResource.setAclUri(serverResource.getAclUri()); clientResource.setDataUri(serverResource.getDataUri()); + clientResource.setInstanceFilesUri(serverResource.getInstanceFilesUri()); clientResource.setDiffUri(serverResource.getDiffUri()); clientResource.setPropertiesUri(serverResource.getPropertiesUri()); clientResource.setSelfUri(serverResource.getSelfUri()); diff --git a/src/main/java/org/opendatakit/aggregate/odktables/impl/api/InstanceFileServiceImpl.java b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/InstanceFileServiceImpl.java index 1deac3382a..9de1201cd5 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/impl/api/InstanceFileServiceImpl.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/InstanceFileServiceImpl.java @@ -16,6 +16,7 @@ package org.opendatakit.aggregate.odktables.impl.api; import java.io.IOException; +import java.util.ArrayList; import java.util.List; import javax.servlet.http.HttpServletRequest; @@ -24,6 +25,7 @@ import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; @@ -42,8 +44,12 @@ import org.opendatakit.aggregate.odktables.api.InstanceFileService; import org.opendatakit.aggregate.odktables.exception.PermissionDeniedException; import org.opendatakit.aggregate.odktables.relation.DbTableInstanceFiles; +import org.opendatakit.aggregate.odktables.rest.ApiConstants; +import org.opendatakit.aggregate.odktables.rest.entity.OdkTablesFileManifest; +import org.opendatakit.aggregate.odktables.rest.entity.OdkTablesFileManifestEntry; import org.opendatakit.aggregate.odktables.rest.entity.TableRole.TablePermission; import org.opendatakit.aggregate.odktables.security.TablesUserPermissions; +import org.opendatakit.common.datamodel.BinaryContent; import org.opendatakit.common.datamodel.BinaryContentManipulator.BlobSubmissionOutcome; import org.opendatakit.common.ermodel.BlobEntitySet; import org.opendatakit.common.persistence.exception.ODKDatastoreException; @@ -80,8 +86,9 @@ public class InstanceFileServiceImpl implements InstanceFileService { private String appId; private String tableId; - public InstanceFileServiceImpl(String appId, String tableId, UriInfo info, TablesUserPermissions userPermissions, CallingContext cc) - throws ODKEntityNotFoundException, ODKDatastoreException { + public InstanceFileServiceImpl(String appId, String tableId, UriInfo info, + TablesUserPermissions userPermissions, CallingContext cc) throws ODKEntityNotFoundException, + ODKDatastoreException { this.cc = cc; this.appId = appId; this.tableId = tableId; @@ -91,108 +98,199 @@ public InstanceFileServiceImpl(String appId, String tableId, UriInfo info, Table @Override @GET - @Path("{filePath:.*}") + @Path("manifest") + @Produces({ MediaType.APPLICATION_JSON, ApiConstants.MEDIA_TEXT_XML_UTF8, + ApiConstants.MEDIA_APPLICATION_XML_UTF8 }) // because we want to get the whole path - public Response getFile(@PathParam("filePath") List segments, @QueryParam(PARAM_AS_ATTACHMENT) String asAttachment, @QueryParam(PARAM_MD5_HASH_ONLY) String asMd5HashOnly) throws IOException { + public Response getManifestAll(@QueryParam(PARAM_AS_ATTACHMENT) String asAttachment) + throws IOException { + // Now construct the path relative to this tableId's instances directory + String partialPath = constructPathFromSegments(new ArrayList()); + + try { + OdkTablesFileManifest manifest = getManifestBase(partialPath); + if (manifest == null) { + return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Unable to retrieve manifest.") + .build(); + } else { + ResponseBuilder rBuild = Response.ok(manifest); + if (asAttachment != null && !"".equals(asAttachment)) { + // Set the filename we're downloading to the disk. + rBuild.header(HtmlConsts.CONTENT_DISPOSITION, "attachment; " + "filename=\"" + + asAttachment + "\""); + } + return rBuild.status(Status.OK).build(); + } + } catch (ODKDatastoreException e) { + e.printStackTrace(); + return Response.status(Status.INTERNAL_SERVER_ERROR) + .entity("Unable to retrieve manifest of attachments for: " + partialPath).build(); + } + } + + @Override + @GET + @Path("manifest/{filePath:.*}") + @Produces({ MediaType.APPLICATION_JSON, ApiConstants.MEDIA_TEXT_XML_UTF8, + ApiConstants.MEDIA_APPLICATION_XML_UTF8 }) + // because we want to get the whole path + public Response getManifest(@PathParam("filePath") List segments, + @QueryParam(PARAM_AS_ATTACHMENT) String asAttachment) throws IOException { + if (segments.size() <= 1) { + return Response.status(Status.BAD_REQUEST).entity(FileService.ERROR_MSG_INSUFFICIENT_PATH) + .build(); + } + // Now construct the path relative to this tableId's instances directory + String partialPath = constructPathFromSegments(segments); + + try { + OdkTablesFileManifest manifest = getManifestBase(partialPath); + if (manifest == null) { + return Response.status(Status.INTERNAL_SERVER_ERROR) + .entity("Unable to retrieve manifest of attachments for: " + partialPath).build(); + } else { + ResponseBuilder rBuild = Response.ok(manifest); + if (asAttachment != null && !"".equals(asAttachment)) { + // Set the filename we're downloading to the disk. + rBuild.header(HtmlConsts.CONTENT_DISPOSITION, "attachment; " + "filename=\"" + + asAttachment + "\""); + } + return rBuild.status(Status.OK).build(); + } + } catch (ODKDatastoreException e) { + e.printStackTrace(); + return Response.status(Status.INTERNAL_SERVER_ERROR) + .entity("Unable to retrieve manifest of attachments for: " + partialPath).build(); + } + } + + // because we want to get the whole path + private OdkTablesFileManifest getManifestBase(String partialPath) throws IOException, + ODKDatastoreException { // The appId and tableId are from the surrounding TableService. - // The entire path from appId onward mimics the path to the instances - // directory By convention, this is: appid/tables/tableid/instances/rest/of/path. - // So we'll reclaim the tidbits and then reconstruct the entire path. + // The partialPath is just instanceId/rest/of/path + // portion of the full app-centric path: + // appid/tables/tableid/instances/instanceId/rest/of/path + + ArrayList manifestEntries = new ArrayList(); + DbTableInstanceFiles blobStore = new DbTableInstanceFiles(tableId, cc); + List matchingContent; + if ( partialPath == null || partialPath.length() == 0 ) { + matchingContent = blobStore.getAllBinaryContents(cc); + } else { + matchingContent = blobStore.getAllMatchingPathPrefixBinaryContents(partialPath, cc); + } + for (BinaryContent bc : matchingContent) { + OdkTablesFileManifestEntry entry = new OdkTablesFileManifestEntry(); + entry.filename = bc.getUnrootedFilePath(); + entry.contentLength = bc.getContentLength(); + entry.contentType = bc.getContentType(); + entry.md5hash = bc.getContentHash(); + entry.downloadUrl = cc.getServerURL() + BasicConsts.FORWARDSLASH + + ServletConsts.ODK_TABLES_SERVLET_BASE_PATH + BasicConsts.FORWARDSLASH + + appId + BasicConsts.FORWARDSLASH + "tables" + BasicConsts.FORWARDSLASH + + tableId + BasicConsts.FORWARDSLASH + "attachments/file/" + entry.filename; + manifestEntries.add(entry); + } + OdkTablesFileManifest manifest = new OdkTablesFileManifest(manifestEntries); + return manifest; + } + + @Override + @GET + @Path("file/{filePath:.*}") + // because we want to get the whole path + public Response getFile(@PathParam("filePath") List segments, + @QueryParam(PARAM_AS_ATTACHMENT) String asAttachment) throws IOException { + // The appId and tableId are from the surrounding TableService. + // The segments are just instanceId/rest/of/path in the full app-centric + // path of: + // appid/tables/tableid/instances/instanceId/rest/of/path if (segments.size() <= 1) { - return Response.status(Status.BAD_REQUEST).entity(FileService.ERROR_MSG_INSUFFICIENT_PATH).build(); + return Response.status(Status.BAD_REQUEST).entity(FileService.ERROR_MSG_INSUFFICIENT_PATH) + .build(); } // Now construct the whole path. - String wholePath = constructPathFromSegments(segments); + String partialPath = constructPathFromSegments(segments); + + String fullPath = appId + "/tables/" + tableId + "/instances/" + partialPath; byte[] fileBlob; String contentType; Long contentLength; try { DbTableInstanceFiles blobStore = new DbTableInstanceFiles(tableId, cc); - BlobEntitySet blobEntitySet = blobStore.getBlobEntitySet(wholePath, cc); + BlobEntitySet blobEntitySet = blobStore.getBlobEntitySet(partialPath, cc); // We should only ever have one, as wholePath is the primary key. if (blobEntitySet.getAttachmentCount(cc) > 1) { - return Response.status(Status.INTERNAL_SERVER_ERROR).entity("More than one file specified for: " + wholePath).build(); + return Response.status(Status.INTERNAL_SERVER_ERROR) + .entity("More than one file specified for: " + partialPath).build(); } if (blobEntitySet.getAttachmentCount(cc) < 1) { - return Response.status(Status.NOT_FOUND).entity("No file found for path: " + wholePath).build(); - } - // just the hash was requested -- return it. Perhaps this should be treated as an ETag? - if ( asMd5HashOnly != null && asMd5HashOnly.length() != 0 ) { - String md5 = blobEntitySet.getContentHash(1, cc); - ResponseBuilder rBuild = Response.ok(md5, MediaType.TEXT_PLAIN); - return rBuild.build(); + return Response.status(Status.NOT_FOUND).entity("No file found for path: " + fullPath) + .build(); } fileBlob = blobEntitySet.getBlob(1, cc); contentType = blobEntitySet.getContentType(1, cc); contentLength = blobEntitySet.getContentLength(1, cc); } catch (ODKDatastoreException e) { e.printStackTrace(); - return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Unable to retrieve attachment and access attributes for: " + wholePath).build(); + return Response.status(Status.INTERNAL_SERVER_ERROR) + .entity("Unable to retrieve attachment and access attributes for: " + fullPath).build(); } // And now prepare everything to be returned to the caller. if (fileBlob != null && contentType != null && contentLength != null && contentLength != 0L) { - ResponseBuilder rBuild = Response.ok(fileBlob, contentType ); + ResponseBuilder rBuild = Response.ok(fileBlob, contentType); if (asAttachment != null && !"".equals(asAttachment)) { // Set the filename we're downloading to the disk. - rBuild.header(HtmlConsts.CONTENT_DISPOSITION, "attachment; " + "filename=\"" + wholePath + rBuild.header(HtmlConsts.CONTENT_DISPOSITION, "attachment; " + "filename=\"" + fullPath + "\""); } - return rBuild.build(); + return rBuild.status(Status.OK).build(); } else { - return Response.status(Status.NOT_FOUND).entity("File content not yet available for: " + wholePath).build(); + return Response.status(Status.NOT_FOUND) + .entity("File content not yet available for: " + fullPath).build(); } } @Override @POST - @Path("{filePath:.*}") - @Consumes({MediaType.MEDIA_TYPE_WILDCARD}) + @Path("file/{filePath:.*}") + @Consumes({ MediaType.MEDIA_TYPE_WILDCARD }) // because we want to get the whole path - public Response putFile(@Context HttpServletRequest req, @PathParam("filePath") List segments, @GZIP byte[] content) throws IOException, ODKTaskLockException { + public Response putFile(@Context HttpServletRequest req, + @PathParam("filePath") List segments, @GZIP byte[] content) throws IOException, + ODKTaskLockException { if (segments.size() <= 1) { - return Response.status(Status.BAD_REQUEST).entity(FileService.ERROR_MSG_INSUFFICIENT_PATH).build(); + return Response.status(Status.BAD_REQUEST).entity(FileService.ERROR_MSG_INSUFFICIENT_PATH) + .build(); } - String wholePath = constructPathFromSegments(segments); + // The appId and tableId are from the surrounding TableService. + // The segments are just instanceId/rest/of/path in the full app-centric + // path of: + // appid/tables/tableid/instances/instanceId/rest/of/path + String partialPath = constructPathFromSegments(segments); String contentType = req.getContentType(); try { userPermissions.checkPermission(appId, tableId, TablePermission.WRITE_ROW); - // We are going to store the file in two tables: 1) a user-friendly table - // that relates an app and table id to the name of a file; 2) a table - // that holds the actual blob. - // - // Table 1 is represented by DbTableFileInfo. Each row of this table - // contains a uri, appid, tableid, and pathToFile. - // Table 2 is a BlobEntitySet. The top level URI of this blob entity set - // is the uri from table 1. Each blob set here has a single attachment - // count of 1--the blob of the file itself. The pathToFile of this - // attachment is null. - // - // So, now that we have retrieved the file from the request, we have two - // things to do: 1) create an entry in the user-friendly table so we can - // bet a uri. 2) add the file to the blob entity set, using the top level - // uri as the row uri from table 1. - // - // 1) Create an entry in the user friendly table. - // Although this is called an entity set, it in fact represents a single - // file, because we have chosen to use it this way in this case. + DbTableInstanceFiles blobStore = new DbTableInstanceFiles(tableId, cc); - BlobEntitySet instance = blobStore.newBlobEntitySet(wholePath, cc); - // TODO: this being set to true is probably where some sort of versioning - // should happen. - BlobSubmissionOutcome outcome = instance.addBlob(content, contentType, null, false, cc); - if ( outcome == BlobSubmissionOutcome.NEW_FILE_VERSION ) { - return Response.status(Status.BAD_REQUEST).entity(ERROR_FILE_VERSION_DIFFERS + "\n" + wholePath).build(); + BlobEntitySet instance = blobStore.newBlobEntitySet(partialPath, cc); + BlobSubmissionOutcome outcome = instance.addBlob(content, contentType, partialPath, false, cc); + if (outcome == BlobSubmissionOutcome.NEW_FILE_VERSION) { + return Response.status(Status.BAD_REQUEST) + .entity(ERROR_FILE_VERSION_DIFFERS + "\n" + partialPath).build(); } - String locationUrl = cc.getServerURL() + BasicConsts.FORWARDSLASH - + ServletConsts.ODK_TABLES_SERVLET_BASE_PATH + BasicConsts.FORWARDSLASH - + wholePath; + String locationUrl = cc.getServerURL() + BasicConsts.FORWARDSLASH + appId + + BasicConsts.FORWARDSLASH + "tables" + BasicConsts.FORWARDSLASH + tableId + + BasicConsts.FORWARDSLASH + "attachments/file/" + partialPath; - return Response.status(Status.CREATED).header("Location",locationUrl).build(); + return Response.status(Status.CREATED).header("Location", locationUrl).build(); } catch (ODKDatastoreException e) { LOGGER.error(("ODKTables file upload persistence error: " + e.getMessage())); - return Response.status(Status.INTERNAL_SERVER_ERROR).entity(ErrorConsts.PERSISTENCE_LAYER_PROBLEM + "\n" + e.getMessage()).build(); + return Response.status(Status.INTERNAL_SERVER_ERROR) + .entity(ErrorConsts.PERSISTENCE_LAYER_PROBLEM + "\n" + e.getMessage()).build(); } catch (PermissionDeniedException e) { LOGGER.error(("ODKTables file upload permissions error: " + e.getMessage())); return Response.status(Status.UNAUTHORIZED).entity("Permission denied").build(); @@ -215,12 +313,6 @@ private String constructPathFromSegments(List segments) { // device. Otherwise you might have to strip the first path segment or do // something similar. StringBuilder sb = new StringBuilder(); - sb.append("tables"); - sb.append(PATH_DELIMITER); - sb.append(tableId); - sb.append(PATH_DELIMITER); - sb.append("instances"); - sb.append(PATH_DELIMITER); int i = 0; for (PathSegment segment : segments) { sb.append(segment.toString()); diff --git a/src/main/java/org/opendatakit/aggregate/odktables/impl/api/TableServiceImpl.java b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/TableServiceImpl.java index 1250669a9a..129f40c02c 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/impl/api/TableServiceImpl.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/TableServiceImpl.java @@ -175,6 +175,7 @@ private TableResource getResource(String appId, TableEntry entry) { URI self = ub.clone().path(TableService.class, "getTable").build(appId, tableId); URI properties = ub.clone().path(TableService.class, "getProperties").build(appId, tableId); URI data = ub.clone().path(TableService.class, "getData").build(appId, tableId); + URI instanceFiles = ub.clone().path(TableService.class, "getInstanceFiles").build(appId, tableId); URI diff = ub.clone().path(TableService.class, "getDiff").build(appId, tableId); URI acl = ub.clone().path(TableService.class, "getAcl").build(appId, tableId); URI definition = ub.clone().path(TableService.class, "getDefinition").build(appId, tableId); @@ -184,6 +185,7 @@ private TableResource getResource(String appId, TableEntry entry) { resource.setDefinitionUri(definition.toASCIIString()); resource.setPropertiesUri(properties.toASCIIString()); resource.setDataUri(data.toASCIIString()); + resource.setInstanceFilesUri(instanceFiles.toASCIIString()); resource.setDiffUri(diff.toASCIIString()); resource.setAclUri(acl.toASCIIString()); return resource; diff --git a/src/main/java/org/opendatakit/aggregate/odktables/relation/DbTableInstanceFiles.java b/src/main/java/org/opendatakit/aggregate/odktables/relation/DbTableInstanceFiles.java index f564ee3f0e..c4f58efa84 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/relation/DbTableInstanceFiles.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/relation/DbTableInstanceFiles.java @@ -16,6 +16,8 @@ package org.opendatakit.aggregate.odktables.relation; +import java.util.Locale; + import org.opendatakit.common.ermodel.AbstractBlobRelationSet; import org.opendatakit.common.persistence.exception.ODKDatastoreException; import org.opendatakit.common.web.CallingContext; @@ -34,8 +36,7 @@ */ public class DbTableInstanceFiles extends AbstractBlobRelationSet { - public DbTableInstanceFiles(String dataTableName, CallingContext cc) throws ODKDatastoreException { - super(dataTableName, cc); + public DbTableInstanceFiles(String tableId, CallingContext cc) throws ODKDatastoreException { + super(tableId.toUpperCase(Locale.ENGLISH) + "_ATT", cc); } - } diff --git a/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/OdkTablesFileManifestEntry.java b/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/OdkTablesFileManifestEntry.java index d0e8d30d3f..dd76afbae5 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/OdkTablesFileManifestEntry.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/OdkTablesFileManifestEntry.java @@ -38,6 +38,12 @@ public class OdkTablesFileManifestEntry { @Attribute(required = true) public String filename; + @Attribute(required = false) + public Long contentLength; + + @Attribute(required = false) + public String contentType; + /** * This is the md5hash of the file, which will be used for checking whether or * not the version of the file on the phone is current. @@ -57,6 +63,8 @@ public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((filename == null) ? 0 : filename.hashCode()); + result = prime * result + ((contentLength == null) ? 0 : contentLength.hashCode()); + result = prime * result + ((contentType == null) ? 0 : contentType.hashCode()); result = prime * result + ((md5hash == null) ? 0 : md5hash.hashCode()); result = prime * result + ((downloadUrl == null) ? 0 : downloadUrl.hashCode()); return result; @@ -75,6 +83,8 @@ public boolean equals(Object obj) { } OdkTablesFileManifestEntry other = (OdkTablesFileManifestEntry) obj; return (filename == null ? other.filename == null : filename.equals(other.filename)) + && (contentLength == null ? other.contentLength == null : contentLength.equals(other.contentLength)) + && (contentType == null ? other.contentType == null : contentType.equals(other.contentType)) && (md5hash == null ? other.md5hash == null : md5hash.equals(other.md5hash)) && (downloadUrl == null ? other.downloadUrl == null : downloadUrl.equals(other.downloadUrl)); } diff --git a/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/TableResource.java b/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/TableResource.java index d1c95e74a9..f99217c5fe 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/TableResource.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/TableResource.java @@ -37,6 +37,9 @@ public class TableResource extends TableEntry { @Element(required = true) private String dataUri; + @Element(required = true) + private String instanceFilesUri; + @Element(required = true) private String diffUri; @@ -70,6 +73,10 @@ public String getDataUri() { return this.dataUri; } + public String getInstanceFilesUri() { + return this.instanceFilesUri; + } + public String getDiffUri() { return this.diffUri; } @@ -98,6 +105,10 @@ public void setDataUri(final String dataUri) { this.dataUri = dataUri; } + public void setInstanceFilesUri(final String instanceFilesUri) { + this.instanceFilesUri = instanceFilesUri; + } + public void setDiffUri(final String diffUri) { this.diffUri = diffUri; } @@ -139,6 +150,11 @@ public boolean equals(Object obj) { return false; } else if (!dataUri.equals(other.dataUri)) return false; + if (instanceFilesUri == null) { + if (other.instanceFilesUri != null) + return false; + } else if (!instanceFilesUri.equals(other.instanceFilesUri)) + return false; if (diffUri == null) { if (other.diffUri != null) return false; @@ -180,6 +196,7 @@ public int hashCode() { result = prime * result + ((displayName == null) ? 0 : displayName.hashCode()); result = prime * result + ((aclUri == null) ? 0 : aclUri.hashCode()); result = prime * result + ((dataUri == null) ? 0 : dataUri.hashCode()); + result = prime * result + ((instanceFilesUri == null) ? 0 : instanceFilesUri.hashCode()); result = prime * result + ((diffUri == null) ? 0 : diffUri.hashCode()); result = prime * result + ((propertiesUri == null) ? 0 : propertiesUri.hashCode()); result = prime * result + ((selfUri == null) ? 0 : selfUri.hashCode()); @@ -203,6 +220,8 @@ public String toString() { builder.append(propertiesUri); builder.append(", dataUri="); builder.append(dataUri); + builder.append(", instanceFilesUri="); + builder.append(instanceFilesUri); builder.append(", diffUri="); builder.append(diffUri); builder.append(", aclUri="); diff --git a/src/main/java/org/opendatakit/aggregate/server/ServerDataServiceImpl.java b/src/main/java/org/opendatakit/aggregate/server/ServerDataServiceImpl.java index 72a96f824f..9a5af0d78c 100644 --- a/src/main/java/org/opendatakit/aggregate/server/ServerDataServiceImpl.java +++ b/src/main/java/org/opendatakit/aggregate/server/ServerDataServiceImpl.java @@ -43,10 +43,12 @@ import org.opendatakit.aggregate.odktables.relation.DbColumnDefinitions; import org.opendatakit.aggregate.odktables.relation.DbTableFileInfo; import org.opendatakit.aggregate.odktables.relation.DbTableFiles; +import org.opendatakit.aggregate.odktables.relation.DbTableInstanceFiles; import org.opendatakit.aggregate.odktables.rest.entity.Row; import org.opendatakit.aggregate.odktables.rest.entity.TableEntry; import org.opendatakit.aggregate.odktables.security.TablesUserPermissions; import org.opendatakit.aggregate.odktables.security.TablesUserPermissionsImpl; +import org.opendatakit.common.datamodel.BinaryContent; import org.opendatakit.common.ermodel.BlobEntitySet; import org.opendatakit.common.persistence.DataField; import org.opendatakit.common.persistence.client.exception.DatastoreFailureException; @@ -456,13 +458,63 @@ public TableContentsClient getTableContents(String tableId) throws AccessDeniedE * adds the correct filename and returns only the non-deleted rows. */ @Override - public TableContentsForFilesClient getFileInfoContents(String tableId) + public TableContentsForFilesClient getAppLevelFileInfoContents() + throws AccessDeniedException, RequestFailureException, DatastoreFailureException, + PermissionDeniedExceptionClient, EntityNotFoundExceptionClient { + TableContentsForFilesClient tcc = new TableContentsForFilesClient(); + HttpServletRequest req = this.getThreadLocalRequest(); + CallingContext cc = ContextFactory.getCallingContext(this, req); + try { + TablesUserPermissions userPermissions = new TablesUserPermissionsImpl(cc.getCurrentUser() + .getUriUser(), cc); + String appId = ServerPreferencesProperties.getOdkTablesAppId(cc); + TableManager tm = new TableManager(appId, userPermissions, cc); + List entities = DbTableFileInfo.queryForAllOdkClientVersionsOfAppLevelFiles(cc); + DbTableFiles dbTableFiles = new DbTableFiles(cc); + + ArrayList completedSummaries = new ArrayList(); + for (DbTableFileInfo.DbTableFileInfoEntity entry : entities) { + BlobEntitySet blobEntitySet = dbTableFiles.getBlobEntitySet(entry.getId(), cc); + if (blobEntitySet.getAttachmentCount(cc) != 1) { + continue; + } + String odkClientVersion = entry.getOdkClientVersion(); + String downloadUrl = cc.getServerURL() + BasicConsts.FORWARDSLASH + + ServletConsts.ODK_TABLES_SERVLET_BASE_PATH + BasicConsts.FORWARDSLASH + + appId + BasicConsts.FORWARDSLASH + FileService.SERVLET_PATH + BasicConsts.FORWARDSLASH + + odkClientVersion + BasicConsts.FORWARDSLASH + entry.getPathToFile() + "?" + FileService.PARAM_AS_ATTACHMENT + "=true"; + FileSummaryClient sum = new FileSummaryClient(entry.getPathToFile(), + blobEntitySet.getContentType(1, cc), + blobEntitySet.getContentLength(1, cc), + entry.getId(), odkClientVersion, "", downloadUrl); + completedSummaries.add(sum); + } + tcc.files = completedSummaries; + return tcc; + } catch (ODKEntityNotFoundException e) { + e.printStackTrace(); + throw new EntityNotFoundExceptionClient(e); + } catch (ODKDatastoreException e) { + e.printStackTrace(); + throw new DatastoreFailureException(e); + } catch (ODKTaskLockException e) { + e.printStackTrace(); + throw new RequestFailureException(e); + } catch (PermissionDeniedException e) { + e.printStackTrace(); + throw new PermissionDeniedExceptionClient(e); + } + } + + /** + * This method more or less gets the user-friendly data to be displayed. It + * adds the correct filename and returns only the non-deleted rows. + */ + @Override + public TableContentsForFilesClient getTableFileInfoContents(String tableId) throws AccessDeniedException, RequestFailureException, DatastoreFailureException, PermissionDeniedExceptionClient, EntityNotFoundExceptionClient { TableContentsForFilesClient tcc = new TableContentsForFilesClient(); - tcc.columnNames = getFileRowInfoColumnNames(); - tcc.columnNames.add(DbTableFileInfo.UI_ONLY_FILENAME_HEADING); - tcc.columnNames.add(DbTableFileInfo.UI_ONLY_TABLENAME_HEADING); HttpServletRequest req = this.getThreadLocalRequest(); CallingContext cc = ContextFactory.getCallingContext(this, req); try { @@ -494,7 +546,65 @@ public TableContentsForFilesClient getFileInfoContents(String tableId) entry.getId(), odkClientVersion, tableId, downloadUrl); completedSummaries.add(sum); } - tcc.nonMediaFiles = completedSummaries; + tcc.files = completedSummaries; + return tcc; + } catch (ODKEntityNotFoundException e) { + e.printStackTrace(); + throw new EntityNotFoundExceptionClient(e); + } catch (ODKDatastoreException e) { + e.printStackTrace(); + throw new DatastoreFailureException(e); + } catch (ODKTaskLockException e) { + e.printStackTrace(); + throw new RequestFailureException(e); + } catch (PermissionDeniedException e) { + e.printStackTrace(); + throw new PermissionDeniedExceptionClient(e); + } + } + + /** + * This method more or less gets the user-friendly data to be displayed. It + * adds the correct filename and returns only the non-deleted rows. + */ + @Override + public TableContentsForFilesClient getInstanceFileInfoContents(String tableId) + throws AccessDeniedException, RequestFailureException, DatastoreFailureException, + PermissionDeniedExceptionClient, EntityNotFoundExceptionClient { + TableContentsForFilesClient tcc = new TableContentsForFilesClient(); + HttpServletRequest req = this.getThreadLocalRequest(); + CallingContext cc = ContextFactory.getCallingContext(this, req); + try { + TablesUserPermissions userPermissions = new TablesUserPermissionsImpl(cc.getCurrentUser() + .getUriUser(), cc); + String appId = ServerPreferencesProperties.getOdkTablesAppId(cc); + TableManager tm = new TableManager(appId, userPermissions, cc); + TableEntry table = tm.getTable(tableId); + if (table == null) { // you couldn't find the table + throw new ODKEntityNotFoundException(); + } + + DbTableInstanceFiles blobStore = new DbTableInstanceFiles(tableId, cc); + List contents = blobStore.getAllBinaryContents(cc); + + ArrayList completedSummaries = new ArrayList(); + for (BinaryContent entry : contents) { + if (entry.getUnrootedFilePath() == null) { + continue; + } + String downloadUrl = cc.getServerURL() + BasicConsts.FORWARDSLASH + + ServletConsts.ODK_TABLES_SERVLET_BASE_PATH + BasicConsts.FORWARDSLASH + + appId + BasicConsts.FORWARDSLASH + "tables" + BasicConsts.FORWARDSLASH + + tableId + BasicConsts.FORWARDSLASH + + + "attachments/file" + BasicConsts.FORWARDSLASH + entry.getUnrootedFilePath() + "?" + FileService.PARAM_AS_ATTACHMENT + "=true"; + FileSummaryClient sum = new FileSummaryClient(entry.getUnrootedFilePath(), + entry.getContentType(), + entry.getContentLength(), + entry.getUri(), null, tableId, downloadUrl); + completedSummaries.add(sum); + } + tcc.files = completedSummaries; return tcc; } catch (ODKEntityNotFoundException e) { e.printStackTrace(); diff --git a/src/main/java/org/opendatakit/aggregate/servlet/OdkTablesAppLevelFileUploadServlet.java b/src/main/java/org/opendatakit/aggregate/servlet/OdkTablesAppLevelFileUploadServlet.java new file mode 100644 index 0000000000..66b9fa7361 --- /dev/null +++ b/src/main/java/org/opendatakit/aggregate/servlet/OdkTablesAppLevelFileUploadServlet.java @@ -0,0 +1,504 @@ +/* + * Copyright (C) 2012-2013 University of Washington + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.opendatakit.aggregate.servlet; + +import java.io.IOException; +import java.io.PrintWriter; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.opendatakit.aggregate.ContextFactory; +import org.opendatakit.aggregate.constants.ServletConsts; +import org.opendatakit.aggregate.constants.common.UIConsts; +import org.opendatakit.aggregate.odktables.impl.api.ServiceUtils; +import org.opendatakit.common.web.CallingContext; + +/** + * This is the servlet that handles the uploading of files that are associted + * with ODKTables tables. It is modeled off of FormUploadServlet.java. + * + * @author sudar.sam@gmail.com + * + */ +public class OdkTablesAppLevelFileUploadServlet extends ServletUtilBase { + + private static final long serialVersionUID = -1173762226947584151L; + + private static final Log logger = LogFactory.getLog(OdkTablesAppLevelFileUploadServlet.class); + + private static final String ADDR = UIConsts.APP_LEVEL_FILE_UPLOAD_SERVLET_ADDR; + + /** + * Title for generated webpage. + */ + public static final String TITLE_INFO = "OdkTables Application Level File Upload"; + + private static final String UPLOAD_PAGE_BODY_START = + + "

Upload a file not associated with a specific tableId

" + + "" + + "
" + " " + " " + /* + * + + * " " + * + + * " " + */+ " \n" + " " + " " + + " " + + " " + + " " + + " \n" + + " " + + " " + + " " + + " \n" + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " \n" + + " \n" + + " " + + " " + + " " + "
"; + private String UPLOAD_PAGE_BODY_MIDDLE_2 = "
" + "
\n" + "
" + + "

Media files that are necessary for any of the uploaded files " + + "(if any) should be in a single directory without subdirectories.

" + "

"; + + @Override + public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { + ServiceUtils.examineRequest(getServletContext(), req); + + CallingContext cc = ContextFactory.getCallingContext(this, req); + + // TODO deal with the javarosa stuff, as in FormUploadServlet's + // corresponding method + + StringBuilder headerString = new StringBuilder(); + headerString.append(""); + headerString.append(""); + headerString.append(""); + headerString.append(""); + + // header info + beginBasicHtmlResponse(TITLE_INFO, headerString.toString(), resp, cc); + addOpenDataKitHeaders(resp); + PrintWriter out = resp.getWriter(); + out.write(UPLOAD_PAGE_BODY_START); + out.write(cc.getWebApplicationURL(ADDR)); + // try { + // String body_middle = UPLOAD_PAGE_BODY_MIDDLE_1 + + // getSelectTableHtml(cc) + UPLOAD_PAGE_BODY_MIDDLE_2; + // out.write(body_middle); + // } catch (ODKDatastoreException e) { + // //TODO fix this + // e.printStackTrace(); + // } + finishBasicHtmlResponse(resp); + } + + /** + * Handler for HTTP head request. This is used to verify that channel security + * and authentication have been properly established when uploading form + * definitions via a program (e.g., Briefcase). + */ + @Override + protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws IOException { + ServiceUtils.examineRequest(getServletContext(), req); + @SuppressWarnings("unused") + CallingContext cc = ContextFactory.getCallingContext(this, req); + logger.info("Inside doHead"); + addOpenDataKitHeaders(resp); + resp.setStatus(204); // no content... + } + + /** + * Handler for HTTP Post request that takes a file and adds it to the + * datastore. + */ + @Override + public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { + ServiceUtils.examineRequest(getServletContext(), req); + // //TODO handle the logging stuff in the friendlytable_log table, that I + // need to + // // create. it also apparently matters which of the tables is written + // first, so + // // be sure to look how Dylan does it first. + // + // // will let the MultiPartFormData files, odk's custom parsing library, + // // handle the parsing. + // + // CallingContext cc = ContextFactory.getCallingContext(this, req); + // + // // verify the requist is multipart, which you have set above in + // // UPLOAD_PAGE_BODY_START + // if (!ServletFileUpload.isMultipartContent(req)) { + // resp.sendError(HttpServletResponse.SC_BAD_REQUEST, + // ErrorConsts.NO_MULTI_PART_CONTENT); + // return; + // } + // + // try { + // // process the form + // MultiPartFormData uploadedFormItems = new MultiPartFormData(req); + // + // // This will house all of the files being uploaded at this time. + // Set> fileSet = + // uploadedFormItems.getFileNameEntrySet(); + // + // + // // "table_file" is what the input was called in the table above. not sure + // // if this is actually what i want it to be here. + // MultiPartFormItem tableFile = uploadedFormItems + // .getFormDataByFieldName("table_file"); + // + // String tableName = uploadedFormItems + // .getSimpleFormField("table_name"); + // + // String fileKey = uploadedFormItems + // .getSimpleFormField("file_key"); + // + // String pathPrefix = uploadedFormItems + // .getSimpleFormField("path_prefix"); + // + // addOpenDataKitHeaders(resp); + // resp.setStatus(HttpServletResponse.SC_CREATED); + // resp.setContentType(HtmlConsts.RESP_TYPE_PLAIN); + // resp.setCharacterEncoding(HtmlConsts.UTF8_ENCODE); + // + // // first we need to get the id of the table. + // String tableId = getTableId(tableName, cc); + // + // // This will store all of the new entities we are adding. They are + // // held in this list first so that we can construct them as they + // // would be exist in the database and then check for conflicts. + // // If there are no conflicts, you move through the entities and + // // call put() to persist them. If you are uploading huge numbers + // // if files, might this be a memory limitation? + // List newEntities = new ArrayList(); + // + // DbTableFiles fileBlobSet = new DbTableFiles(cc); + // BlobEntitySet instance = fileBlobSet.newBlobEntitySet(cc); + // // TODO: for now this is the filename, but eventually i will want to have + // another + // // input field that is "path prefix" that will tell you how to prefix + // // the file additions. + // instance.addBlob(tableFile.getStream().toByteArray(), + // tableFile.getContentType(), tableFile.getFilename(), false, cc); + // + // // now we want to put the pertinent information into the user-friendly + // table. + // // why he didn't make this a util class I do not know. + // EntityCreator ec = new EntityCreator(); + // Entity newRow = ec.newTableFileInfoEntity(tableId, + // DbTableFileInfo.Type.FILE.name, + // fileKey, instance.getUri(), false, cc); + // newEntities.add(newRow); + // + // // now let's handle the media files. + // // This will be the suffix for the media files. + // int i = 1; + // for (Map.Entry itm : fileSet) { + // if (itm.getKey().equals(tableFile.getFilename())) + // continue; // ignore the file name, which we have already added + // fileBlobSet = new DbTableFiles(cc); + // instance = fileBlobSet.newBlobEntitySet(cc); + // // TODO: for now this is the filename, but eventually i will want to have + // another + // // input field that is "path prefix" that will tell you how to prefix + // // the file additions. + // String tempFileName; + // if (pathPrefix.equals("")) { + // tempFileName = itm.getValue().getFilename(); + // } else { + // tempFileName = pathPrefix + "/" + itm.getValue().getFilename(); + // } + // + // instance.addBlob(itm.getValue().getStream().toByteArray(), + // itm.getValue().getContentType(), tempFileName, false, cc); + // + // // now we want to put the pertinent information into the user-friendly + // table. + // String tempFileKey = fileKey + "_" + String.valueOf(i); + // // why he didn't make this a util class I do not know. + // newRow = ec.newTableFileInfoEntity(tableId, + // DbTableFileInfo.Type.FILE.name, + // tempFileKey, instance.getUri(), true, cc); + // newEntities.add(newRow); + // i++; + // } + // + // // now check for collisions. + // List existingRows = getFileRows(req, tableId); + // List newRows = EntityConverter.toRowsFromFileInfo(newEntities); + // List conflictingKeys = getDuplicateKeys(existingRows, + // newRows); + // List conflictingFiles = getDuplicateFileNames(cc, + // existingRows, newRows); + // // first and foremost, you have to have a key. + // fileKey = uploadedFormItems.getSimpleFormField("file_key"); + // if (tableFile.getFilename().equals("")) { + // PrintWriter out = resp.getWriter(); + // out.write(HtmlConsts.HTML_OPEN); + // out.write(HtmlConsts.BODY_OPEN); + // out.write("

No files have been uploaded!

"); + // out.write("

You must select a file to upload.

"); + // out.write("

Click "); + // out.write(HtmlUtil.createHref(cc.getWebApplicationURL(ADDR), "here", + // false)); + // out.write(" to return try again.

"); + // out.write(HtmlConsts.BODY_CLOSE); + // out.write(HtmlConsts.HTML_CLOSE); + // } else if (fileKey.equals("")) { + // PrintWriter out = resp.getWriter(); + // out.write(HtmlConsts.HTML_OPEN); + // out.write(HtmlConsts.BODY_OPEN); + // out.write("

No files have been uploaded!

"); + // out.write("

You must enter a key for the file.

"); + // out.write("

Click "); + // out.write(HtmlUtil.createHref(cc.getWebApplicationURL(ADDR), "here", + // false)); + // out.write(" to return try again.

"); + // out.write(HtmlConsts.BODY_CLOSE); + // out.write(HtmlConsts.HTML_CLOSE); + // } else if (conflictingKeys.size() == 0 && conflictingFiles.size() == 0) { + // // persist all the entities + // for (Entity entity : newEntities) + // entity.put(cc); + // PrintWriter out = resp.getWriter(); + // out.write(HtmlConsts.HTML_OPEN); + // out.write(HtmlConsts.BODY_OPEN); + // out.write("

Successful file upload.

"); + // out.write("

Click "); + // out.write(HtmlUtil.createHref(cc.getWebApplicationURL(ADDR), "here", + // false)); + // out.write(" to return to add another file.

"); + // out.write(HtmlConsts.BODY_CLOSE); + // out.write(HtmlConsts.HTML_CLOSE); + // } else { + // // we have to notify the client of conflicts + // PrintWriter out = resp.getWriter(); + // out.write(HtmlConsts.HTML_OPEN); + // out.write(HtmlConsts.BODY_OPEN); + // out.write("

No files have been uploaded!

"); + // out.write("

Doing so would result in conflicts and the loss" + + // " of information.

"); + // if (conflictingKeys.size() != 0) { + // out.write("

The following keys already exist:

"); + // for (String key : conflictingKeys) { + // out.write("

   " + key + "

"); + // } + // } + // if (conflictingFiles.size() != 0) { + // out.write("

The following files already exist:

"); + // for (String file : conflictingFiles) { + // out.write("

   " + file + "

"); + // } + // } + // out.write("

You must resolve these conflicts.

"); + // out.write("

Click "); + // out.write(HtmlUtil.createHref(cc.getWebApplicationURL(ADDR), "here", + // false)); + // out.write(" to return try again.

"); + // out.write(HtmlConsts.BODY_CLOSE); + // out.write(HtmlConsts.HTML_CLOSE); + // } + // + // + // } catch (ODKDatastoreException e) { + // logger.error("File upload persistence error: " + e.getMessage()); + // e.printStackTrace(); + // resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, + // ErrorConsts.PERSISTENCE_LAYER_PROBLEM + "\n" + e.getMessage()); + // } catch (FileUploadException e) { + // logger.error("File upload persistence error: " + e.getMessage()); + // e.printStackTrace(resp.getWriter()); + // resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, + // ErrorConsts.UPLOAD_PROBLEM); + // } catch (DatastoreFailureException e) { + // logger.error("File upload persistence error: " + e.getMessage()); + // e.printStackTrace(resp.getWriter()); + // resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, + // ErrorConsts.PERSISTENCE_LAYER_PROBLEM + "\n" + e.getMessage()); + // } catch (RequestFailureException e) { + // logger.error("Error uploading files due to error checking current" + + // " contents of the datastores: " + e.getMessage()); + // e.printStackTrace(resp.getWriter()); + // resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, + // ErrorConsts.PERSISTENCE_LAYER_PROBLEM + "\n" + e.getMessage()); + // } catch (AccessDeniedException e) { + // // I don't think this should happen, as by the time you've gotten + // // to this servlet, you should have already had appropriate + // // permissions to see this table and access things. + // logger.error("Error uploading files, access denied when accessing" + + // " the datastore: " + e.getMessage()); + // e.printStackTrace(resp.getWriter()); + // resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, + // ErrorConsts.PERSISTENCE_LAYER_PROBLEM + "\n" + e.getMessage()); + // } + + } + + // /** + // * Get the rows in DbTableFileInfo for the corresponding table key. + // */ + // private List getFileRows(HttpServletRequest req, String tableId) + // throws AccessDeniedException, RequestFailureException, + // DatastoreFailureException { + // CallingContext cc = ContextFactory.getCallingContext(this, req); + // try { + // List rows = EntityConverter.toRowsFromFileInfo( + // DbTableFileInfo.query(tableId, cc)); + // return rows; + // } catch (ODKDatastoreException e) { + // e.printStackTrace(); + // throw new DatastoreFailureException(e); + // } + // } + // + // /** + // * This method returns the duplicate keys that are preventing the current + // * upload. + // * @param newRows the new rows you are adding + // * @param oldRows the rows already in the database + // * @return the conflicting keys + // */ + // private List getDuplicateKeys(List newRows, List oldRows) + // { + // List duplicates = new ArrayList(); + // // First we're going to convert the current rows to a set based on their + // // keys. + // Set currentKeys = new HashSet(); + // for (Row oldRow : oldRows) { + // Map values = oldRow.getValues(); + // String oldKey = values.get(DbTableFileInfo.KEY); + // currentKeys.add(oldKey); + // } + // // Now check that the list of new keys doesn't have any collisions. + // for (Row newRow : newRows) { + // Map values = newRow.getValues(); + // String newKey = values.get(DbTableFileInfo.KEY); + // if (currentKeys.contains(newKey)) + // duplicates.add(newKey); + // } + // return duplicates; + // } + // + // /** + // * This method returns the duplicate filenames that would occur on the + // * current upload. + // * @param newRows the new rows you are adding + // * @param oldRows the rows already in the database + // * @return the conflicting keys + // */ + // private List getDuplicateFileNames(CallingContext cc, + // List newRows, List oldRows) + // throws ODKDatastoreException { + // List duplicates = new ArrayList(); + // // First we're going to convert the current rows to a set based on their + // // keys. + // DbTableFiles blobSetRelation = new DbTableFiles(cc); + // Set currentFiles = new HashSet(); + // for (Row oldRow : oldRows) { + // Map values = oldRow.getValues(); + // // we only want to look at the rows that represent files. + // if (values.get(DbTableFileInfo.VALUE_TYPE) + // .equals(DbTableFileInfo.Type.FILE.name)) { + // String oldFile = blobSetRelation.getBlobEntitySet( + // oldRow.getValues().get(DbTableFileInfo.VALUE), cc) + // .getUnrootedFilename(1, cc); + // currentFiles.add(oldFile); + // } + // } + // // Now check that the list of new keys doesn't have any collisions. + // for (Row newRow : newRows) { + // Map values = newRow.getValues(); + // // we only want to look at the rows that represent files. + // if (values.get(DbTableFileInfo.VALUE_TYPE) + // .equals(DbTableFileInfo.Type.FILE.name)) { + // String newFile = blobSetRelation.getBlobEntitySet( + // newRow.getValues().get(DbTableFileInfo.VALUE), cc) + // .getUnrootedFilename(1, cc); + // if (currentFiles.contains(newFile)) + // duplicates.add(newFile); + // } + // } + // return duplicates; + // } + // + // /** + // * This generates the HTML for a selector box to allow the selection of + // * a table name from the list of available tables. + // * @param cc + // * @return + // */ + // private String getSelectTableHtml(CurrentUserPermissions userPermissions, + // CallingContext cc) + // throws ODKDatastoreException { + // TableManager tm = new TableManager(userPermissions, cc); + // List entries = tm.getTables(); + // String html = ""; + // return html; + // } + +} diff --git a/src/main/java/org/opendatakit/aggregate/servlet/OdkTablesTableFileUploadServlet.java b/src/main/java/org/opendatakit/aggregate/servlet/OdkTablesTableFileUploadServlet.java index a83982e944..e60c251c94 100644 --- a/src/main/java/org/opendatakit/aggregate/servlet/OdkTablesTableFileUploadServlet.java +++ b/src/main/java/org/opendatakit/aggregate/servlet/OdkTablesTableFileUploadServlet.java @@ -48,14 +48,15 @@ public class OdkTablesTableFileUploadServlet extends ServletUtilBase { // i'm not sure if this is the correct place/way to be adding mine. private static final String ADDR = UIConsts.TABLE_FILE_UPLOAD_SERVLET_ADDR; + private static final String TABLE_ID = "table_id"; /** * Title for generated webpage. */ - public static final String TITLE_INFO = "OdkTables File Upload"; + public static final String TITLE_INFO = "OdkTables TableId-Specific File Upload"; private static final String UPLOAD_PAGE_BODY_START = - "

Upload a file associated with a table

" + "

Upload a file associated with a tableId

" + "" + "
" * + * " " - */+ " \n" + " " + " "; + */+ " \n" + " " + " "; private String UPLOAD_PAGE_BODY_MIDDLE_2 = "" + " " + " " diff --git a/src/main/java/org/opendatakit/common/ermodel/AbstractBlobRelationSet.java b/src/main/java/org/opendatakit/common/ermodel/AbstractBlobRelationSet.java index 4a391209bd..12c1527631 100644 --- a/src/main/java/org/opendatakit/common/ermodel/AbstractBlobRelationSet.java +++ b/src/main/java/org/opendatakit/common/ermodel/AbstractBlobRelationSet.java @@ -24,6 +24,7 @@ import org.opendatakit.common.persistence.CommonFieldsBase; import org.opendatakit.common.persistence.Datastore; import org.opendatakit.common.persistence.Query; +import org.opendatakit.common.persistence.Query.FilterOperation; import org.opendatakit.common.persistence.exception.ODKDatastoreException; import org.opendatakit.common.persistence.exception.ODKEntityPersistException; import org.opendatakit.common.persistence.exception.ODKOverQuotaException; @@ -394,6 +395,30 @@ public BlobSubmissionOutcome addBlob(byte[] byteArray, String contentType, } } + public List getAllBinaryContents(CallingContext cc) throws ODKDatastoreException { + Query q = cc.getDatastore().createQuery(ctntRelation, "getAllContents", cc.getCurrentUser()); + List bc = (List) q.executeQuery(); + return bc; + } + + public List getAllMatchingPathPrefixBinaryContents(String pathPrefix, CallingContext cc) throws ODKDatastoreException { + // first try for an exact match + Query q = cc.getDatastore().createQuery(ctntRelation, "getAllContents", cc.getCurrentUser()); + q.addFilter(ctntRelation.unrootedFilePath, FilterOperation.EQUAL, pathPrefix); + List bcExact = (List) q.executeQuery(); + // now try for a partial match + q = cc.getDatastore().createQuery(ctntRelation, "getAllContents", cc.getCurrentUser()); + if ( !pathPrefix.endsWith("/") ) { + pathPrefix = pathPrefix + "/"; + } + q.addFilter(ctntRelation.unrootedFilePath, FilterOperation.GREATER_THAN_OR_EQUAL, pathPrefix); + String endPrefix = pathPrefix.substring(0,pathPrefix.length()-1) + "0"; // ('/'+1) == '0' in ASCII + q.addFilter(ctntRelation.unrootedFilePath, FilterOperation.LESS_THAN, endPrefix); + List bc = (List) q.executeQuery(); + bcExact.addAll(bc); + return bcExact; + } + @SuppressWarnings("unused") private final TableNamespace namespace; private final String backingBaseTableName; diff --git a/src/main/resources/common/security.properties b/src/main/resources/common/security.properties index adea58a322..f34f60719e 100644 --- a/src/main/resources/common/security.properties +++ b/src/main/resources/common/security.properties @@ -25,7 +25,7 @@ security.server.channelType=REQUIRES_INSECURE_CHANNEL # external services. # # This is configured during install. If blank, discovers an IP address -#security.server.hostname= +#security.server.hostname=192.168.15.200 security.server.hostname=sprite.cs.washington.edu # # any port pairings can be used. diff --git a/src/test/java/org/opendatakit/aggregate/odktables/entity/SerializationTest.java b/src/test/java/org/opendatakit/aggregate/odktables/entity/SerializationTest.java index abd283a9f4..3ed68fc3ce 100644 --- a/src/test/java/org/opendatakit/aggregate/odktables/entity/SerializationTest.java +++ b/src/test/java/org/opendatakit/aggregate/odktables/entity/SerializationTest.java @@ -165,6 +165,7 @@ public void testTableResource() throws Exception { TableResource expected = new TableResource(entry); expected.setSelfUri("http://localhost:8080/odktables/tables/1"); expected.setDataUri("http://localhost:8080/odktables/tables/1/rows"); + expected.setInstanceFilesUri("http://localhost:8080/odktables/tables/1/attachments"); expected.setPropertiesUri("http://localhost:8080/odktables/tables/1/columns"); expected.setDefinitionUri("http://localhost:8080/odktables/tables/1/definition"); expected.setDiffUri("http://localhost:8080/odktables/tables/1/rows/diff"); From 5b5ccd218a2cf34bc2dd925aaa296eab9007c08c Mon Sep 17 00:00:00 2001 From: mitchellsundt Date: Tue, 29 Apr 2014 16:01:49 -0700 Subject: [PATCH 08/13] Update to latest javarosa jar. Fix savepoint_type not-being-written bug and integration tests. --- eclipse-aggregate-gae/.classpath | 3 ++- .../war/WEB-INF/lib/javarosa-libraries-2014-03-10.jar | 3 --- .../war/WEB-INF/lib/javarosa-libraries-2014-04-29.jar | 3 +++ pom.xml | 2 +- .../aggregate/odktables/api/DataServiceTest.java | 5 ++++- .../java/org/opendatakit/aggregate/odktables/api/T.java | 8 ++++++-- .../aggregate/odktables/api/perf/CreateTableTest.java | 6 ++++-- .../aggregate/odktables/api/perf/MultipleUsersTest.java | 5 ++++- .../aggregate/odktables/relation/EntityConverter.java | 1 + src/main/libs/javarosa-libraries-2014-03-10.jar | 3 --- src/main/libs/javarosa-libraries-2014-04-29.jar | 3 +++ src/main/libs/readme.txt | 2 +- 12 files changed, 29 insertions(+), 15 deletions(-) delete mode 100644 eclipse-aggregate-gae/war/WEB-INF/lib/javarosa-libraries-2014-03-10.jar create mode 100644 eclipse-aggregate-gae/war/WEB-INF/lib/javarosa-libraries-2014-04-29.jar delete mode 100644 src/main/libs/javarosa-libraries-2014-03-10.jar create mode 100644 src/main/libs/javarosa-libraries-2014-04-29.jar diff --git a/eclipse-aggregate-gae/.classpath b/eclipse-aggregate-gae/.classpath index 905a88c582..db6b6561c8 100644 --- a/eclipse-aggregate-gae/.classpath +++ b/eclipse-aggregate-gae/.classpath @@ -1,6 +1,7 @@ + @@ -47,7 +48,7 @@ - + diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/javarosa-libraries-2014-03-10.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/javarosa-libraries-2014-03-10.jar deleted file mode 100644 index af66b59246..0000000000 --- a/eclipse-aggregate-gae/war/WEB-INF/lib/javarosa-libraries-2014-03-10.jar +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a7b9d7ebe2b58f3ac2cf803c29d009dcc3ef5a98d341c684be7cb9226297c5b9 -size 437988 diff --git a/eclipse-aggregate-gae/war/WEB-INF/lib/javarosa-libraries-2014-04-29.jar b/eclipse-aggregate-gae/war/WEB-INF/lib/javarosa-libraries-2014-04-29.jar new file mode 100644 index 0000000000..79f06d1a99 --- /dev/null +++ b/eclipse-aggregate-gae/war/WEB-INF/lib/javarosa-libraries-2014-04-29.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4a66d790d77b68a2923de626dc1fd2cb44d7cd703ed0579e6044f79a85ea5ec4 +size 437972 diff --git a/pom.xml b/pom.xml index 077eec5a38..3fbbb0c678 100644 --- a/pom.xml +++ b/pom.xml @@ -541,7 +541,7 @@ org.javarosa javarosa-libraries - 2014-03-10 + 2014-04-29 jar compile diff --git a/src/it/java/org/opendatakit/aggregate/odktables/api/DataServiceTest.java b/src/it/java/org/opendatakit/aggregate/odktables/api/DataServiceTest.java index 6d79411597..ae2f8d4930 100644 --- a/src/it/java/org/opendatakit/aggregate/odktables/api/DataServiceTest.java +++ b/src/it/java/org/opendatakit/aggregate/odktables/api/DataServiceTest.java @@ -9,8 +9,10 @@ import org.junit.Before; import org.junit.Test; +import org.opendatakit.aggregate.odktables.rest.SavepointTypeManipulator; import org.opendatakit.aggregate.odktables.rest.entity.Row; import org.opendatakit.aggregate.odktables.rest.entity.RowResource; +import org.opendatakit.aggregate.odktables.rest.entity.Scope; import org.opendatakit.aggregate.odktables.rest.entity.TableResource; import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; @@ -36,7 +38,8 @@ public void testInsertRow() { String rowId = T.Data.DYLAN.getId(); String uri = Util.buildUri(baseUri.toASCIIString(), rowId); - Row expected = Row.forInsert(rowId, T.form_id_1, T.locale_1, T.savepoint_timestamp_1, T.savepoint_creator_1, T.Data.DYLAN.getValues()); + Row expected = Row.forInsert(rowId, T.form_id_1, T.locale_1, SavepointTypeManipulator.complete(), + T.savepoint_timestamp_1, T.savepoint_creator_1, Scope.EMPTY_SCOPE, T.Data.DYLAN.getValues()); HttpEntity entity = super.entity(expected); ResponseEntity resp = rt.exchange(uri, HttpMethod.PUT, entity, RowResource.class); diff --git a/src/it/java/org/opendatakit/aggregate/odktables/api/T.java b/src/it/java/org/opendatakit/aggregate/odktables/api/T.java index 6adf9f64d6..e839473ab5 100644 --- a/src/it/java/org/opendatakit/aggregate/odktables/api/T.java +++ b/src/it/java/org/opendatakit/aggregate/odktables/api/T.java @@ -5,9 +5,11 @@ import java.util.List; import java.util.Map; import org.junit.Ignore; +import org.opendatakit.aggregate.odktables.rest.SavepointTypeManipulator; import org.opendatakit.aggregate.odktables.rest.TableConstants; import org.opendatakit.aggregate.odktables.rest.entity.Column; import org.opendatakit.aggregate.odktables.rest.entity.Row; +import org.opendatakit.aggregate.odktables.rest.entity.Scope; @Ignore public class T { @@ -36,8 +38,10 @@ public class T { @SuppressWarnings("serial") public static final List rows = new ArrayList() { { - add(Row.forInsert(T.Data.DYLAN.getId(), T.form_id_1, T.locale_1, T.savepoint_timestamp_1, T.savepoint_creator_1, T.Data.DYLAN.getValues())); - add(Row.forInsert(T.Data.JOHN.getId(), T.form_id_2, T.locale_2, T.savepoint_timestamp_2, T.savepoint_creator_2, T.Data.JOHN.getValues())); + add(Row.forInsert(T.Data.DYLAN.getId(), T.form_id_1, T.locale_1, SavepointTypeManipulator.complete(), + T.savepoint_timestamp_1, T.savepoint_creator_1, Scope.EMPTY_SCOPE, T.Data.DYLAN.getValues())); + add(Row.forInsert(T.Data.JOHN.getId(), T.form_id_2, T.locale_2, SavepointTypeManipulator.complete(), + T.savepoint_timestamp_2, T.savepoint_creator_2, Scope.EMPTY_SCOPE, T.Data.JOHN.getValues())); } }; diff --git a/src/it/java/org/opendatakit/aggregate/odktables/api/perf/CreateTableTest.java b/src/it/java/org/opendatakit/aggregate/odktables/api/perf/CreateTableTest.java index ed4aeaf015..b2e8bc2d11 100644 --- a/src/it/java/org/opendatakit/aggregate/odktables/api/perf/CreateTableTest.java +++ b/src/it/java/org/opendatakit/aggregate/odktables/api/perf/CreateTableTest.java @@ -13,9 +13,11 @@ import org.opendatakit.aggregate.odktables.api.perf.AggregateSynchronizer; import org.opendatakit.aggregate.odktables.api.perf.AggregateSynchronizer.InvalidAuthTokenException; import org.opendatakit.aggregate.odktables.api.perf.PerfTest; +import org.opendatakit.aggregate.odktables.rest.SavepointTypeManipulator; import org.opendatakit.aggregate.odktables.rest.entity.Column; import org.opendatakit.aggregate.odktables.rest.entity.Row; import org.opendatakit.aggregate.odktables.rest.entity.RowResource; +import org.opendatakit.aggregate.odktables.rest.entity.Scope; import org.springframework.web.client.HttpStatusCodeException; import com.google.common.collect.Lists; @@ -63,11 +65,11 @@ public void run() { for (int j = 0; j < numCols; j++) { values.put(colName(j), "value_" + j); } - Row row = Row.forInsert(UUID.randomUUID().toString(), T.form_id_1, T.locale_1, T.savepoint_timestamp_1, T.savepoint_creator_1, values); + Row row = Row.forInsert(UUID.randomUUID().toString(), T.form_id_1, T.locale_1, SavepointTypeManipulator.complete(), + T.savepoint_timestamp_1, T.savepoint_creator_1, Scope.EMPTY_SCOPE, values); RowResource inserted = synchronizer.putRow(tableId, row); rows.add(inserted); } - // update rows for (Row row : rows) { Map values = row.getValues(); diff --git a/src/it/java/org/opendatakit/aggregate/odktables/api/perf/MultipleUsersTest.java b/src/it/java/org/opendatakit/aggregate/odktables/api/perf/MultipleUsersTest.java index 00529af019..c0a2fc5737 100644 --- a/src/it/java/org/opendatakit/aggregate/odktables/api/perf/MultipleUsersTest.java +++ b/src/it/java/org/opendatakit/aggregate/odktables/api/perf/MultipleUsersTest.java @@ -18,9 +18,11 @@ import org.opendatakit.aggregate.odktables.api.T; import org.opendatakit.aggregate.odktables.api.perf.AggregateSynchronizer; import org.opendatakit.aggregate.odktables.api.perf.PerfTest; +import org.opendatakit.aggregate.odktables.rest.SavepointTypeManipulator; import org.opendatakit.aggregate.odktables.rest.entity.Column; import org.opendatakit.aggregate.odktables.rest.entity.Row; import org.opendatakit.aggregate.odktables.rest.entity.Error; +import org.opendatakit.aggregate.odktables.rest.entity.Scope; import org.opendatakit.aggregate.odktables.rest.entity.Error.ErrorType; import org.opendatakit.aggregate.odktables.rest.serialization.SimpleXMLSerializerForAggregate; import org.simpleframework.xml.Serializer; @@ -64,7 +66,8 @@ public void run() { for (int j = 0; j < numCols; j++) { values.put(colName(j), "value_" + j); } - Row row = Row.forInsert(UUID.randomUUID().toString(), T.form_id_1, T.locale_1, T.savepoint_timestamp_1, T.savepoint_creator_1, values); + Row row = Row.forInsert(UUID.randomUUID().toString(), T.form_id_1, T.locale_1, SavepointTypeManipulator.complete(), + T.savepoint_timestamp_1, T.savepoint_creator_1, Scope.EMPTY_SCOPE, values); synchronizer.putRow(tableId, row); tryAgain = false; } catch (HttpStatusCodeException e) { diff --git a/src/main/java/org/opendatakit/aggregate/odktables/relation/EntityConverter.java b/src/main/java/org/opendatakit/aggregate/odktables/relation/EntityConverter.java index a375357ecf..c68dd8978f 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/relation/EntityConverter.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/relation/EntityConverter.java @@ -301,6 +301,7 @@ public Row toRow(Entity entity, List columns) { row.setFormId(entity.getString(DbTable.FORM_ID)); row.setLocale(entity.getString(DbTable.LOCALE)); + row.setSavepointType(entity.getString(DbTable.SAVEPOINT_TYPE)); row.setSavepointTimestamp(entity.getString(DbTable.SAVEPOINT_TIMESTAMP)); row.setSavepointCreator(entity.getString(DbTable.SAVEPOINT_CREATOR)); diff --git a/src/main/libs/javarosa-libraries-2014-03-10.jar b/src/main/libs/javarosa-libraries-2014-03-10.jar deleted file mode 100644 index af66b59246..0000000000 --- a/src/main/libs/javarosa-libraries-2014-03-10.jar +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a7b9d7ebe2b58f3ac2cf803c29d009dcc3ef5a98d341c684be7cb9226297c5b9 -size 437988 diff --git a/src/main/libs/javarosa-libraries-2014-04-29.jar b/src/main/libs/javarosa-libraries-2014-04-29.jar new file mode 100644 index 0000000000..79f06d1a99 --- /dev/null +++ b/src/main/libs/javarosa-libraries-2014-04-29.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4a66d790d77b68a2923de626dc1fd2cb44d7cd703ed0579e6044f79a85ea5ec4 +size 437972 diff --git a/src/main/libs/readme.txt b/src/main/libs/readme.txt index 8b2d1ad9cc..773133e742 100644 --- a/src/main/libs/readme.txt +++ b/src/main/libs/readme.txt @@ -7,7 +7,7 @@ This is a special build of javarosa using the tree at https://bitbucket.org/m.su It incorporates multithread-safe KoBo collect changes (from Clayton), abandons J2ME support, exposes bind and prompt attributes, and numerous contributed fixes from SurveyCTO and others. -mvn install:install-file -Dfile=javarosa-libraries-2014-03-10.jar -DgroupId=org.javarosa -DartifactId=javarosa-libraries -Dversion=2014-03-10 -Dpackaging=jar +mvn install:install-file -Dfile=javarosa-libraries-2014-04-29.jar -DgroupId=org.javarosa -DartifactId=javarosa-libraries -Dversion=2014-04-29 -Dpackaging=jar odk-httpclient-gae: From 38a81758631b320bc57aa7c4b1e2bc05b4fca359 Mon Sep 17 00:00:00 2001 From: mitchellsundt Date: Thu, 1 May 2014 17:08:27 -0700 Subject: [PATCH 09/13] Remove the KVS properties from the tables sync protocol. --- eclipse-aggregate-gae/.classpath | 1 - eclipse-aggregate-gae/war/WEB-INF/web.xml | 10 - .../odktables/api/PropertiesServiceTest.java | 78 ------ .../api/perf/AggregateSynchronizer.java | 30 --- .../OdkTablesManageInstanceFilesSubTab.java | 4 +- .../OdkTablesManageTableFilesSubTab.java | 4 +- .../client/OdkTablesViewTableSubTab.java | 4 +- .../aggregate/client/SecureGWT.java | 48 ++-- .../OdkTablesKeyValueStoreEntryClient.java | 43 --- .../odktables/PropertiesResourceClient.java | 109 -------- .../odktables/ServerPropertiesService.java | 47 ---- .../ServerPropertiesServiceAsync.java | 28 -- .../odktables/TableDefinitionClient.java | 17 +- .../client/odktables/TableEntryClient.java | 46 +--- .../odktables/TablePropertiesClient.java | 94 ------- .../client/odktables/TableResourceClient.java | 20 +- .../client/odktables/TableTypeClient.java | 33 --- .../popups/OdkTablesAddNewTablePopup.java | 19 +- .../client/table/OdkTablesTableList.java | 15 +- .../widgets/OdkTablesShowTableButton.java | 2 +- .../widgets/TableEntryClientListBox.java | 12 +- .../odktables/PropertiesManager.java | 236 ----------------- .../aggregate/odktables/TableManager.java | 18 +- .../aggregate/odktables/api/FileService.java | 16 +- .../odktables/api/PropertiesService.java | 68 ----- .../aggregate/odktables/api/TableService.java | 11 - .../odktables/entity/UtilTransforms.java | 133 +--------- .../OdkTablesKeyValueManifestManager.java | 98 ------- .../odktables/impl/api/FileServiceImpl.java | 68 ++++- .../impl/api/InstanceFileServiceImpl.java | 2 +- .../impl/api/PropertiesServiceImpl.java | 81 ------ .../odktables/impl/api/TableServiceImpl.java | 15 -- .../odktables/importexport/CsvUtil.java | 2 + .../odktables/relation/DbKeyValueStore.java | 248 ------------------ .../odktables/relation/DbTableEntry.java | 52 +--- .../odktables/relation/EntityConverter.java | 39 +-- .../odktables/relation/EntityCreator.java | 55 +--- .../entity/OdkTablesFileManifestEntry.java | 3 +- .../entity/OdkTablesKeyValueStoreEntry.java | 128 --------- .../rest/entity/PropertiesResource.java | 96 ------- .../odktables/rest/entity/TableEntry.java | 21 +- .../rest/entity/TableProperties.java | 141 ---------- .../odktables/rest/entity/TableResource.java | 40 +-- .../aggregate/server/ServerOdkTablesUtil.java | 45 +--- .../server/ServerPropertiesServiceImpl.java | 113 -------- .../server/ServerTableServiceImpl.java | 15 +- .../aggregate/odktables/AuthFilterTest.java | 4 - .../aggregate/odktables/DataManagerTest.java | 4 - .../odktables/PropertiesManagerTest.java | 194 -------------- .../opendatakit/aggregate/odktables/T.java | 59 ----- .../odktables/TableAclManagerTest.java | 4 - .../aggregate/odktables/TableManagerTest.java | 35 +-- .../odktables/entity/ManifestTest.java | 180 ------------- .../odktables/entity/SerializationTest.java | 41 +-- 54 files changed, 154 insertions(+), 2775 deletions(-) delete mode 100644 src/it/java/org/opendatakit/aggregate/odktables/api/PropertiesServiceTest.java delete mode 100644 src/main/java/org/opendatakit/aggregate/client/odktables/OdkTablesKeyValueStoreEntryClient.java delete mode 100644 src/main/java/org/opendatakit/aggregate/client/odktables/PropertiesResourceClient.java delete mode 100644 src/main/java/org/opendatakit/aggregate/client/odktables/ServerPropertiesService.java delete mode 100644 src/main/java/org/opendatakit/aggregate/client/odktables/ServerPropertiesServiceAsync.java delete mode 100644 src/main/java/org/opendatakit/aggregate/client/odktables/TablePropertiesClient.java delete mode 100644 src/main/java/org/opendatakit/aggregate/client/odktables/TableTypeClient.java delete mode 100644 src/main/java/org/opendatakit/aggregate/odktables/PropertiesManager.java delete mode 100644 src/main/java/org/opendatakit/aggregate/odktables/api/PropertiesService.java delete mode 100644 src/main/java/org/opendatakit/aggregate/odktables/entity/serialization/OdkTablesKeyValueManifestManager.java delete mode 100644 src/main/java/org/opendatakit/aggregate/odktables/impl/api/PropertiesServiceImpl.java delete mode 100644 src/main/java/org/opendatakit/aggregate/odktables/relation/DbKeyValueStore.java delete mode 100644 src/main/java/org/opendatakit/aggregate/odktables/rest/entity/OdkTablesKeyValueStoreEntry.java delete mode 100644 src/main/java/org/opendatakit/aggregate/odktables/rest/entity/PropertiesResource.java delete mode 100644 src/main/java/org/opendatakit/aggregate/odktables/rest/entity/TableProperties.java delete mode 100644 src/main/java/org/opendatakit/aggregate/server/ServerPropertiesServiceImpl.java delete mode 100644 src/test/java/org/opendatakit/aggregate/odktables/PropertiesManagerTest.java delete mode 100644 src/test/java/org/opendatakit/aggregate/odktables/entity/ManifestTest.java diff --git a/eclipse-aggregate-gae/.classpath b/eclipse-aggregate-gae/.classpath index db6b6561c8..a44f369be5 100644 --- a/eclipse-aggregate-gae/.classpath +++ b/eclipse-aggregate-gae/.classpath @@ -1,7 +1,6 @@ - diff --git a/eclipse-aggregate-gae/war/WEB-INF/web.xml b/eclipse-aggregate-gae/war/WEB-INF/web.xml index c6028a780e..33299b721b 100644 --- a/eclipse-aggregate-gae/war/WEB-INF/web.xml +++ b/eclipse-aggregate-gae/war/WEB-INF/web.xml @@ -129,16 +129,6 @@ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ /aggregateui/serverdiffservice - - serverPropertiesServiceImpl - org.opendatakit.aggregate.server.ServerPropertiesServiceImpl - - - - serverPropertiesServiceImpl - /aggregateui/serverpropertiesservice - - serverTableACLServiceImpl org.opendatakit.aggregate.server.ServerTableACLServiceImpl diff --git a/src/it/java/org/opendatakit/aggregate/odktables/api/PropertiesServiceTest.java b/src/it/java/org/opendatakit/aggregate/odktables/api/PropertiesServiceTest.java deleted file mode 100644 index f944b8e55e..0000000000 --- a/src/it/java/org/opendatakit/aggregate/odktables/api/PropertiesServiceTest.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.opendatakit.aggregate.odktables.api; - -import static org.junit.Assert.assertEquals; - -import java.net.URI; -import java.util.ArrayList; -import java.util.List; - -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.opendatakit.aggregate.odktables.api.AbstractServiceTest; -import org.opendatakit.aggregate.odktables.api.T; -import org.opendatakit.aggregate.odktables.rest.KeyValueStoreConstants; -import org.opendatakit.aggregate.odktables.rest.entity.OdkTablesKeyValueStoreEntry; -import org.opendatakit.aggregate.odktables.rest.entity.TableProperties; -import org.opendatakit.aggregate.odktables.rest.entity.PropertiesResource; -import org.opendatakit.aggregate.odktables.rest.entity.TableResource; -import org.springframework.http.HttpMethod; -import org.springframework.http.ResponseEntity; - -public class PropertiesServiceTest extends AbstractServiceTest { - - @Before - public void setUp() throws Exception { - super.createTable(); - TableResource resource = rt.getForObject(baseUri.resolve(T.tableId), TableResource.class); - baseUri = URI.create(resource.getPropertiesUri()); - } - - @Test - public void testGetProperties() { - PropertiesResource properties = rt.getForObject(baseUri, PropertiesResource.class); - assertEquals(T.tableId, properties.getTableId()); - // TODO: fix this! - // assertEquals(T.tableMetadata, properties.getMetadata()); - } - - // TODO: fix this -- once we straighten out TableKey and TableId - @Ignore - public void testSetTableName() { - String expected = T.displayName + " a different name"; - - PropertiesResource resource = rt.getForObject(baseUri, PropertiesResource.class); - TableProperties properties = resource; - properties.setTableId(expected); - - ResponseEntity response = rt.exchange(baseUri, HttpMethod.PUT, - entity(properties), PropertiesResource.class); - resource = response.getBody(); - assertEquals(expected, resource.getTableId()); - } - - @Test - public void testSetTableMetadata() { - ArrayList list = new ArrayList(); - OdkTablesKeyValueStoreEntry entry = new OdkTablesKeyValueStoreEntry(); - entry.partition = KeyValueStoreConstants.PARTITION_TABLE; - entry.aspect = "testing"; - entry.key = "value"; - entry.type = "text"; - list.add(entry); - PropertiesResource resource = rt.getForObject(baseUri, PropertiesResource.class); - TableProperties properties = resource; - properties.setKeyValueStoreEntries(list); - - ResponseEntity response = rt.exchange(baseUri, HttpMethod.PUT, - entity(properties), PropertiesResource.class); - resource = response.getBody(); - - List returnedList = resource.getKeyValueStoreEntries(); - assertEquals(list.size(), returnedList.size()); - for ( int i = 0 ; i < list.size() ; ++i ) { - assertEquals( list.get(i), returnedList.get(i)); - } - } - -} diff --git a/src/it/java/org/opendatakit/aggregate/odktables/api/perf/AggregateSynchronizer.java b/src/it/java/org/opendatakit/aggregate/odktables/api/perf/AggregateSynchronizer.java index 08c289d05e..6657a02f56 100644 --- a/src/it/java/org/opendatakit/aggregate/odktables/api/perf/AggregateSynchronizer.java +++ b/src/it/java/org/opendatakit/aggregate/odktables/api/perf/AggregateSynchronizer.java @@ -23,14 +23,10 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import org.opendatakit.aggregate.odktables.rest.KeyValueStoreConstants; import org.opendatakit.aggregate.odktables.rest.entity.Column; -import org.opendatakit.aggregate.odktables.rest.entity.OdkTablesKeyValueStoreEntry; -import org.opendatakit.aggregate.odktables.rest.entity.PropertiesResource; import org.opendatakit.aggregate.odktables.rest.entity.Row; import org.opendatakit.aggregate.odktables.rest.entity.RowResource; import org.opendatakit.aggregate.odktables.rest.entity.TableDefinition; -import org.opendatakit.aggregate.odktables.rest.entity.TableProperties; import org.opendatakit.aggregate.odktables.rest.entity.TableResource; import org.opendatakit.aggregate.odktables.rest.interceptor.AggregateRequestInterceptor; import org.opendatakit.aggregate.odktables.rest.serialization.OdkJsonHttpMessageConverter; @@ -195,32 +191,6 @@ public void deleteRow(String tableId, String rowId) throws IOException { } } - public PropertiesResource setTableProperties(String schemaETag, String propertiesETag, String tableId) throws IOException { - TableResource resource = getResource(tableId); - - ArrayList keyValueStoreEntries = new ArrayList(); - OdkTablesKeyValueStoreEntry entry = new OdkTablesKeyValueStoreEntry(); - entry.partition = KeyValueStoreConstants.PARTITION_TABLE; - entry.aspect = "metadata"; - entry.key = "my_value"; - entry.type = "text"; - entry.value = "aValue"; - keyValueStoreEntries.add(entry); - // put new properties - TableProperties properties = new TableProperties(schemaETag, propertiesETag, tableId, keyValueStoreEntries); - HttpEntity entity = new HttpEntity(properties, requestHeaders); - ResponseEntity updatedEntity; - try { - updatedEntity = rt.exchange(resource.getPropertiesUri(), HttpMethod.PUT, entity, - PropertiesResource.class); - } catch (ResourceAccessException e) { - throw new IOException(e.getMessage()); - } - PropertiesResource propsResource = updatedEntity.getBody(); - - return propsResource; - } - public class InvalidAuthTokenException extends Exception { private static final long serialVersionUID = 1L; diff --git a/src/main/java/org/opendatakit/aggregate/client/OdkTablesManageInstanceFilesSubTab.java b/src/main/java/org/opendatakit/aggregate/client/OdkTablesManageInstanceFilesSubTab.java index 91f252ecc6..a185f8a158 100644 --- a/src/main/java/org/opendatakit/aggregate/client/OdkTablesManageInstanceFilesSubTab.java +++ b/src/main/java/org/opendatakit/aggregate/client/OdkTablesManageInstanceFilesSubTab.java @@ -214,7 +214,7 @@ public void addTablesToListBox(ArrayList tables) { tableBox.clear(); tableBox.addItem(""); // blank holder to start with no selection for (int i = 0; i < currentTables.size(); i++) { - tableBox.addItem(currentTables.get(i).getDisplayName()); + tableBox.addItem(currentTables.get(i).getTableId()); } } @@ -235,7 +235,7 @@ public void updateContentsForSelectedTable() { tableFileData.updateDisplay(currentTable); selectTablePanel.setHTML(2, 0, "

Displaying:

"); - selectTablePanel.setHTML(2, 1, "

" + currentTable.getDisplayName() + selectTablePanel.setHTML(2, 1, "

" + currentTable.getTableId() + "

"); add(tableFileData); } diff --git a/src/main/java/org/opendatakit/aggregate/client/OdkTablesManageTableFilesSubTab.java b/src/main/java/org/opendatakit/aggregate/client/OdkTablesManageTableFilesSubTab.java index ba0e7e94ee..c5317828bb 100644 --- a/src/main/java/org/opendatakit/aggregate/client/OdkTablesManageTableFilesSubTab.java +++ b/src/main/java/org/opendatakit/aggregate/client/OdkTablesManageTableFilesSubTab.java @@ -217,7 +217,7 @@ public void addTablesToListBox(ArrayList tables) { tableBox.clear(); tableBox.addItem(""); // blank holder to start with no selection for (int i = 0; i < currentTables.size(); i++) { - tableBox.addItem(currentTables.get(i).getDisplayName()); + tableBox.addItem(currentTables.get(i).getTableId()); } } @@ -238,7 +238,7 @@ public void updateContentsForSelectedTable() { tableFileData.updateDisplay(currentTable); selectTablePanel.setHTML(2, 0, "

Displaying:

"); - selectTablePanel.setHTML(2, 1, "

" + currentTable.getDisplayName() + selectTablePanel.setHTML(2, 1, "

" + currentTable.getTableId() + "

"); add(tableFileData); } diff --git a/src/main/java/org/opendatakit/aggregate/client/OdkTablesViewTableSubTab.java b/src/main/java/org/opendatakit/aggregate/client/OdkTablesViewTableSubTab.java index 8efc3fe24f..240a3bb934 100644 --- a/src/main/java/org/opendatakit/aggregate/client/OdkTablesViewTableSubTab.java +++ b/src/main/java/org/opendatakit/aggregate/client/OdkTablesViewTableSubTab.java @@ -198,7 +198,7 @@ public void addTablesToListBox(ArrayList tables) { tableBox.clear(); tableBox.addItem(""); // blank holder to start with no selection for (int i = 0; i < currentTables.size(); i++) { - tableBox.addItem(currentTables.get(i).getDisplayName()); + tableBox.addItem(currentTables.get(i).getTableId()); } } @@ -221,7 +221,7 @@ public void updateContentsForSelectedTable() { tableData.updateDisplay(currentTable); selectTablePanel.setHTML(1, 0, "

Displaying:

"); - selectTablePanel.setHTML(1, 1, "

" + currentTable.getDisplayName() + selectTablePanel.setHTML(1, 1, "

" + currentTable.getTableId() + "

"); add(tableData); } diff --git a/src/main/java/org/opendatakit/aggregate/client/SecureGWT.java b/src/main/java/org/opendatakit/aggregate/client/SecureGWT.java index a94a845b4b..4f391646dd 100644 --- a/src/main/java/org/opendatakit/aggregate/client/SecureGWT.java +++ b/src/main/java/org/opendatakit/aggregate/client/SecureGWT.java @@ -28,8 +28,6 @@ import org.opendatakit.aggregate.client.odktables.ServerDataServiceAsync; import org.opendatakit.aggregate.client.odktables.ServerDiffService; import org.opendatakit.aggregate.client.odktables.ServerDiffServiceAsync; -import org.opendatakit.aggregate.client.odktables.ServerPropertiesService; -import org.opendatakit.aggregate.client.odktables.ServerPropertiesServiceAsync; import org.opendatakit.aggregate.client.odktables.ServerTableACLService; import org.opendatakit.aggregate.client.odktables.ServerTableACLServiceAsync; import org.opendatakit.aggregate.client.odktables.ServerTableService; @@ -58,15 +56,14 @@ * AccessDeniedException back up through the GWT RPC mechanism. Without the * header, the failed requests would be redirected to an access-denied.html * static page. - * + * * @author mitchellsundt@gmail.com - * + * */ public class SecureGWT { public enum ServiceType { - FILTER, FORM, FORM_ADMIN, PREFERENCE, SECURITY, SECURITY_ADMIN, SERVICES_ADMIN, SUBMISSION, - ODK_TABLES_ADMIN, ODK_TABLES_DATA, ODK_TABLES_DIFF, ODK_TABLES_PROPERTIES, - ODK_TABLES_ACL, ODK_TABLES_TABLE; + FILTER, FORM, FORM_ADMIN, PREFERENCE, SECURITY, SECURITY_ADMIN, SERVICES_ADMIN, SUBMISSION, + ODK_TABLES_ADMIN, ODK_TABLES_DATA, ODK_TABLES_DIFF, ODK_TABLES_ACL, ODK_TABLES_TABLE; } private static SecureGWT singleton = null; @@ -98,12 +95,11 @@ protected RequestBuilder doCreate(String serviceEntryPoint) { /** site admin... */ private SecurityAdminServiceAsync securityAdminServiceAsync = null; private OdkTablesAdminServiceAsync odkTablesAdminServiceAsync = null; - + /** odkTables... */ private ServerDataServiceAsync serverDataServiceAsync = null; private ServerDiffServiceAsync serverDiffServiceAsync = null; - private ServerPropertiesServiceAsync serverPropertiesServiceAsync = null; - private ServerTableACLServiceAsync serverTableACLServiceAsync = null; + private ServerTableACLServiceAsync serverTableACLServiceAsync = null; private ServerTableServiceAsync serverTableServiceAsync = null; private SecureGWT() { @@ -116,10 +112,9 @@ private SecureGWT() { servicesAdminServiceAsync = (ServicesAdminServiceAsync) create(ServiceType.SERVICES_ADMIN); securityAdminServiceAsync = (SecurityAdminServiceAsync) create(ServiceType.SECURITY_ADMIN); odkTablesAdminServiceAsync = (OdkTablesAdminServiceAsync) create(ServiceType.ODK_TABLES_ADMIN); - //tables stuff: + //tables stuff: serverDataServiceAsync = (ServerDataServiceAsync) create(ServiceType.ODK_TABLES_DATA); serverDiffServiceAsync = (ServerDiffServiceAsync) create(ServiceType.ODK_TABLES_DIFF); - serverPropertiesServiceAsync = (ServerPropertiesServiceAsync) create(ServiceType.ODK_TABLES_PROPERTIES); serverTableACLServiceAsync = (ServerTableACLServiceAsync) create(ServiceType.ODK_TABLES_ACL); serverTableServiceAsync = (ServerTableServiceAsync) create(ServiceType.ODK_TABLES_TABLE); }; @@ -159,25 +154,21 @@ public SecurityAdminServiceAsync getSecurityAdminServiceAsync() { public OdkTablesAdminServiceAsync getOdkTablesAdminServiceAsync() { return odkTablesAdminServiceAsync; } - + // odk tables stuff: - + public ServerDataServiceAsync getServerDataServiceAsync() { return serverDataServiceAsync; } - + public ServerDiffServiceAsync getServerDiffServiceAsync() { return serverDiffServiceAsync; } - - public ServerPropertiesServiceAsync getServerPropertiesServiceAsync() { - return serverPropertiesServiceAsync; - } - + public ServerTableACLServiceAsync getTableACLServiceAsync() { return serverTableACLServiceAsync; } - + public ServerTableServiceAsync getTableServiceAsync() { return serverTableServiceAsync; } @@ -225,9 +216,6 @@ private Object create(ServiceType type) { case ODK_TABLES_DIFF: obj = GWT.create(ServerDiffService.class); break; - case ODK_TABLES_PROPERTIES: - obj = GWT.create(ServerPropertiesService.class); - break; case ODK_TABLES_ACL: obj = GWT.create(ServerTableACLService.class); break; @@ -286,26 +274,22 @@ public static SecurityAdminServiceAsync getSecurityAdminService() { public static OdkTablesAdminServiceAsync getOdkTablesAdminService() { return get().getOdkTablesAdminServiceAsync(); } - + /** odk tables: these should be any user, as the actual data and whatnot should be managed * by the tables code itself, to account for the more refined control levels. */ public static ServerDataServiceAsync getServerDataService() { return get().serverDataServiceAsync; } - + public static ServerDiffServiceAsync getServerDiffService() { return get().serverDiffServiceAsync; } - - public static ServerPropertiesServiceAsync getServerPropertiesService() { - return get().serverPropertiesServiceAsync; - } - + public static ServerTableACLServiceAsync getServerTableACLServiceAsync() { return get().serverTableACLServiceAsync; } - + public static ServerTableServiceAsync getServerTableService() { return get().serverTableServiceAsync; } diff --git a/src/main/java/org/opendatakit/aggregate/client/odktables/OdkTablesKeyValueStoreEntryClient.java b/src/main/java/org/opendatakit/aggregate/client/odktables/OdkTablesKeyValueStoreEntryClient.java deleted file mode 100644 index 1964ed4199..0000000000 --- a/src/main/java/org/opendatakit/aggregate/client/odktables/OdkTablesKeyValueStoreEntryClient.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2013 University of Washington - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.opendatakit.aggregate.client.odktables; - -import java.io.Serializable; - -/** - * - * @author sudar.sam@gmail.com - * - */ -public class OdkTablesKeyValueStoreEntryClient implements Serializable { - - /** - * - */ - private static final long serialVersionUID = -4019279442687770191L; - - public OdkTablesKeyValueStoreEntryClient() { - // necessary for gwt serialization - } - - public String tableId; - public String partition; - public String aspect; - public String key; - public String type; - public String value; -} diff --git a/src/main/java/org/opendatakit/aggregate/client/odktables/PropertiesResourceClient.java b/src/main/java/org/opendatakit/aggregate/client/odktables/PropertiesResourceClient.java deleted file mode 100644 index ddf1c8f569..0000000000 --- a/src/main/java/org/opendatakit/aggregate/client/odktables/PropertiesResourceClient.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2013 University of Washington - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.opendatakit.aggregate.client.odktables; - -import java.io.Serializable; - -/** - * This is the client-side version of - * org.opendatakit.aggregate.odktables.entity.api.PropertiesResource.java.
- * The idea is that this will serve the same function as the server-side object, - * but for the client. It is possible that a similar object might have to be - * created on the server-side to handle the non-phone requests, but this will - * hopefully become apparent. - * - * @author sudar.sam@gmail.com - * - */ -public class PropertiesResourceClient extends TablePropertiesClient implements Serializable { - - /** - * - */ - private static final long serialVersionUID = -30163347528571037L; - private String selfUri; - private String tableUri; - - @SuppressWarnings("unused") - private PropertiesResourceClient() { - // necessary for gwt serialization - } - - public PropertiesResourceClient(TablePropertiesClient tableProperties) { - super(); - setSchemaETag(tableProperties.getSchemaETag()); - setPropertiesETag(tableProperties.getPropertiesETag()); - setTableId(tableProperties.getTableId()); - setKeyValueStoreEntries(tableProperties.getKeyValueStoreEntries()); - } - - public String getSelfUri() { - return this.selfUri; - } - - public String getTableUri() { - return this.tableUri; - } - - public void setSelfUri(final String selfUri) { - this.selfUri = selfUri; - } - - public void setTableUri(final String tableUri) { - this.tableUri = tableUri; - } - - @Override - public boolean equals(final Object o) { - if (o == this) - return true; - if (!(o instanceof PropertiesResourceClient)) - return false; - final PropertiesResourceClient other = (PropertiesResourceClient) o; - if (!other.canEqual((java.lang.Object) this)) - return false; - if (!super.equals(o)) - return false; - if (this.getSelfUri() == null ? other.getSelfUri() != null : !this.getSelfUri().equals( - (java.lang.Object) other.getSelfUri())) - return false; - if (this.getTableUri() == null ? other.getTableUri() != null : !this.getTableUri().equals( - (java.lang.Object) other.getTableUri())) - return false; - return true; - } - - public boolean canEqual(final Object other) { - return other instanceof PropertiesResourceClient; - } - - @Override - public int hashCode() { - final int PRIME = 31; - int result = 1; - result = result * PRIME + super.hashCode(); - result = result * PRIME + (this.getSelfUri() == null ? 0 : this.getSelfUri().hashCode()); - result = result * PRIME + (this.getTableUri() == null ? 0 : this.getTableUri().hashCode()); - return result; - } - - public String toString() { - return "PropertiesResource(super=" + super.toString() + ", selfUri=" + this.getSelfUri() - + ", tableUri=" + this.getTableUri() + ")"; - } - -} diff --git a/src/main/java/org/opendatakit/aggregate/client/odktables/ServerPropertiesService.java b/src/main/java/org/opendatakit/aggregate/client/odktables/ServerPropertiesService.java deleted file mode 100644 index 63fe5d4790..0000000000 --- a/src/main/java/org/opendatakit/aggregate/client/odktables/ServerPropertiesService.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2013 University of Washington - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.opendatakit.aggregate.client.odktables; - -import org.opendatakit.aggregate.client.exception.ETagMismatchExceptionClient; -import org.opendatakit.aggregate.client.exception.PermissionDeniedExceptionClient; -import org.opendatakit.aggregate.client.exception.RequestFailureException; -import org.opendatakit.common.persistence.client.exception.DatastoreFailureException; -import org.opendatakit.common.security.client.exception.AccessDeniedException; - -import com.google.gwt.user.client.rpc.RemoteService; -import com.google.gwt.user.client.rpc.RemoteServiceRelativePath; - -/** - * This will be the PropertiesService for the server. It will act the same way - * as org.opendatakit.aggregate.odktables.api.PropertiesService, except that it - * will be for interacting with the table information on the server, rather than - * with a phone. - * - * @author sudar.sam - */ - -@RemoteServiceRelativePath("serverpropertiesservice") -public interface ServerPropertiesService extends RemoteService { - - TablePropertiesClient getProperties(String tableId) throws AccessDeniedException, - RequestFailureException, DatastoreFailureException, PermissionDeniedExceptionClient; - - TablePropertiesClient setProperties(TablePropertiesClient properties, String tableId) - throws AccessDeniedException, RequestFailureException, DatastoreFailureException, - ETagMismatchExceptionClient, PermissionDeniedExceptionClient; - -} diff --git a/src/main/java/org/opendatakit/aggregate/client/odktables/ServerPropertiesServiceAsync.java b/src/main/java/org/opendatakit/aggregate/client/odktables/ServerPropertiesServiceAsync.java deleted file mode 100644 index 5a0d9e6ca2..0000000000 --- a/src/main/java/org/opendatakit/aggregate/client/odktables/ServerPropertiesServiceAsync.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2013 University of Washington - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.opendatakit.aggregate.client.odktables; - -import com.google.gwt.user.client.rpc.AsyncCallback; - -public interface ServerPropertiesServiceAsync { - - void getProperties(String tableId, AsyncCallback callback); - - void setProperties(TablePropertiesClient properties, String tableId, - AsyncCallback callback); - -} diff --git a/src/main/java/org/opendatakit/aggregate/client/odktables/TableDefinitionClient.java b/src/main/java/org/opendatakit/aggregate/client/odktables/TableDefinitionClient.java index c27f2e9953..5659d29c57 100644 --- a/src/main/java/org/opendatakit/aggregate/client/odktables/TableDefinitionClient.java +++ b/src/main/java/org/opendatakit/aggregate/client/odktables/TableDefinitionClient.java @@ -37,8 +37,6 @@ public class TableDefinitionClient implements Serializable { private static final long serialVersionUID = -113634509888543150L; private String tableId; - private String displayName; - private TableTypeClient type; private ArrayList columns; @SuppressWarnings("unused") @@ -46,12 +44,9 @@ private TableDefinitionClient() { // necessary for gwt serialization } - public TableDefinitionClient(final String tableId, final ArrayList columns, - final String displayName, final TableTypeClient type) { + public TableDefinitionClient(final String tableId, final ArrayList columns) { this.tableId = tableId; this.columns = columns; - this.displayName = displayName; - this.type = type; } public String getTableId() { @@ -62,14 +57,6 @@ public ArrayList getColumns() { return this.columns; } - public String getDisplayName() { - return this.displayName; - } - - public TableTypeClient getType() { - return this.type; - } - public void setColumns(final ArrayList columns) { this.columns = columns; } @@ -77,6 +64,6 @@ public void setColumns(final ArrayList columns) { @Override public String toString() { return "TableDefinitionClient[tableId=" + getTableId() + ", columns=" + getColumns().toString() - + ", displayName=" + getDisplayName() + ", type=" + getType() + "]"; + + "]"; } } diff --git a/src/main/java/org/opendatakit/aggregate/client/odktables/TableEntryClient.java b/src/main/java/org/opendatakit/aggregate/client/odktables/TableEntryClient.java index d0c2e55ae7..fbd0ffc06d 100644 --- a/src/main/java/org/opendatakit/aggregate/client/odktables/TableEntryClient.java +++ b/src/main/java/org/opendatakit/aggregate/client/odktables/TableEntryClient.java @@ -36,20 +36,16 @@ public class TableEntryClient implements Serializable { private static final long serialVersionUID = -70945438534365403L; private String tableId; - private String displayName; private String dataETag; - private String propertiesETag; private String schemaETag; protected TableEntryClient() { } - public TableEntryClient(final String tableId, final String displayName, final String dataETag, - final String propertiesETag, final String schemaETag) { + public TableEntryClient(final String tableId, final String dataETag, + final String schemaETag) { this.tableId = tableId; - this.displayName = displayName; this.dataETag = dataETag; - this.propertiesETag = propertiesETag; this.schemaETag = schemaETag; } @@ -61,14 +57,6 @@ public void setTableId(final String tableId) { this.tableId = tableId; } - public String getDisplayName() { - return this.displayName; - } - - public void setDisplayName(final String displayName) { - this.displayName = displayName; - } - public String getDataETag() { return this.dataETag; } @@ -77,14 +65,6 @@ public void setDataETag(final String dataETag) { this.dataETag = dataETag; } - public String getPropertiesETag() { - return propertiesETag; - } - - public void setPropertiesETag(String propertiesETag) { - this.propertiesETag = propertiesETag; - } - public String getSchemaETag() { return schemaETag; } @@ -111,14 +91,6 @@ public boolean equals(Object obj) { return false; } - if (propertiesETag == null) { - if (other.propertiesETag != null) { - return false; - } - } else if (!propertiesETag.equals(other.propertiesETag)) { - return false; - } - if (schemaETag == null) { if (other.schemaETag != null) { return false; @@ -135,14 +107,6 @@ public boolean equals(Object obj) { return false; } - if (displayName == null) { - if (other.displayName != null) { - return false; - } - } else if (!displayName.equals(other.displayName)) { - return false; - } - return true; } @@ -151,16 +115,14 @@ public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((dataETag == null) ? 0 : dataETag.hashCode()); - result = prime * result + ((propertiesETag == null) ? 0 : propertiesETag.hashCode()); result = prime * result + ((schemaETag == null) ? 0 : schemaETag.hashCode()); result = prime * result + ((tableId == null) ? 0 : tableId.hashCode()); - result = prime * result + ((displayName == null) ? 0 : displayName.hashCode()); return result; } @Override public String toString() { - return "TableEntry [tableId=" + tableId + ", displayName=" + displayName + ", dataETag=" - + dataETag + ", propertiesETag=" + propertiesETag + ", schemaETag=" + schemaETag + "]"; + return "TableEntry [tableId=" + tableId + ", dataETag=" + + dataETag + ", schemaETag=" + schemaETag + "]"; } } \ No newline at end of file diff --git a/src/main/java/org/opendatakit/aggregate/client/odktables/TablePropertiesClient.java b/src/main/java/org/opendatakit/aggregate/client/odktables/TablePropertiesClient.java deleted file mode 100644 index ed074baa29..0000000000 --- a/src/main/java/org/opendatakit/aggregate/client/odktables/TablePropertiesClient.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2013 University of Washington - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.opendatakit.aggregate.client.odktables; - -import java.io.Serializable; -import java.util.ArrayList; - -/** - * This is the client-side version of - * org.opendatakit.aggregate.odktables.entity.TableProperties.java.
- * The idea is that this will do the same thing, but for the server. The common - * caveats apply for all of these objects, in that it is not yet clear how they - * will work, and if there will need to be similar objects that are NOT the - * original for-the-phone objects on the server. - * - * @author sudar.sam@gmail.com - * - */ -public class TablePropertiesClient implements Serializable { - - /** - * - */ - private static final long serialVersionUID = 1977461163068996L; - - private String schemaETag; - private String propertiesETag; - private String tableId; - private ArrayList kvsEntries; - - protected TablePropertiesClient() { - } - - public TablePropertiesClient(String schemaETag, String propertiesETag, String tableId, - ArrayList kvsEntries) { - this.schemaETag = schemaETag; - this.propertiesETag = propertiesETag; - this.tableId = tableId; - this.kvsEntries = kvsEntries; - } - - public String getSchemaETag() { - return schemaETag; - } - - public void setSchemaETag(String schemaETag) { - this.schemaETag = schemaETag; - } - - public String getPropertiesETag() { - return propertiesETag; - } - - public void setPropertiesETag(String propertiesETag) { - this.propertiesETag = propertiesETag; - } - - public String getTableId() { - return tableId; - } - - public void setTableId(String tableId) { - this.tableId = tableId; - } - - public ArrayList getKeyValueStoreEntries() { - return this.kvsEntries; - } - - public void setKeyValueStoreEntries(ArrayList kvsEntries) { - this.kvsEntries = kvsEntries; - } - - @Override - public String toString() { - return "TableProperties [schemaETag=" + schemaETag + ", propertiesETag=" + propertiesETag + ", tableId=" + tableId - + ", kvsEntries=" + kvsEntries.toString() + "]"; - } - -} diff --git a/src/main/java/org/opendatakit/aggregate/client/odktables/TableResourceClient.java b/src/main/java/org/opendatakit/aggregate/client/odktables/TableResourceClient.java index d8a630c8eb..b6cc03a157 100644 --- a/src/main/java/org/opendatakit/aggregate/client/odktables/TableResourceClient.java +++ b/src/main/java/org/opendatakit/aggregate/client/odktables/TableResourceClient.java @@ -35,15 +35,13 @@ public class TableResourceClient extends TableEntryClient implements Serializabl private static final long serialVersionUID = 81481336521796890L; private String selfUri; private String definitionUri; - private String propertiesUri; private String dataUri; private String instanceFilesUri; private String diffUri; private String aclUri; public TableResourceClient(TableEntryClient entry) { - super(entry.getTableId(), entry.getDisplayName(), entry.getDataETag(), entry - .getPropertiesETag(), entry.getSchemaETag()); + super(entry.getTableId(), entry.getDataETag(), entry.getSchemaETag()); } @SuppressWarnings("unused") @@ -59,10 +57,6 @@ public String getDefinitionUri() { return this.definitionUri; } - public String getPropertiesUri() { - return this.propertiesUri; - } - public String getDataUri() { return this.dataUri; } @@ -87,10 +81,6 @@ public void setDefinitionUri(final String definitionUri) { this.definitionUri = definitionUri; } - public void setPropertiesUri(final String propertiesUri) { - this.propertiesUri = propertiesUri; - } - public void setDataUri(final String dataUri) { this.dataUri = dataUri; } @@ -141,11 +131,6 @@ public boolean equals(Object obj) { return false; } else if (!diffUri.equals(other.diffUri)) return false; - if (propertiesUri == null) { - if (other.propertiesUri != null) - return false; - } else if (!propertiesUri.equals(other.propertiesUri)) - return false; if (selfUri == null) { if (other.selfUri != null) return false; @@ -171,7 +156,6 @@ public int hashCode() { result = prime * result + ((dataUri == null) ? 0 : dataUri.hashCode()); result = prime * result + ((instanceFilesUri == null) ? 0 : instanceFilesUri.hashCode()); result = prime * result + ((diffUri == null) ? 0 : diffUri.hashCode()); - result = prime * result + ((propertiesUri == null) ? 0 : propertiesUri.hashCode()); result = prime * result + ((selfUri == null) ? 0 : selfUri.hashCode()); return result; } @@ -186,8 +170,6 @@ public String toString() { StringBuilder builder = new StringBuilder(); builder.append("TableResource [selfUri="); builder.append(selfUri); - builder.append(", propertiesUri="); - builder.append(propertiesUri); builder.append(", dataUri="); builder.append(dataUri); builder.append(", instanceFilesUri="); diff --git a/src/main/java/org/opendatakit/aggregate/client/odktables/TableTypeClient.java b/src/main/java/org/opendatakit/aggregate/client/odktables/TableTypeClient.java deleted file mode 100644 index 03e95bb71e..0000000000 --- a/src/main/java/org/opendatakit/aggregate/client/odktables/TableTypeClient.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2013 University of Washington - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.opendatakit.aggregate.client.odktables; - -import java.io.Serializable; - -/** - * Type of tables. The representation field is the magic string that has to - * correspond to the correct use on the phone. - *

- * It is living in the client-side code just because it's not an important - * enough object to have to transform. - * - * @author sudars - * - */ -public enum TableTypeClient implements Serializable { - DATA, SECURITY, SHORTCUT; -} diff --git a/src/main/java/org/opendatakit/aggregate/client/popups/OdkTablesAddNewTablePopup.java b/src/main/java/org/opendatakit/aggregate/client/popups/OdkTablesAddNewTablePopup.java index b122f7d9dc..b1fb17f4a9 100644 --- a/src/main/java/org/opendatakit/aggregate/client/popups/OdkTablesAddNewTablePopup.java +++ b/src/main/java/org/opendatakit/aggregate/client/popups/OdkTablesAddNewTablePopup.java @@ -23,12 +23,10 @@ import org.opendatakit.aggregate.client.odktables.ColumnClient; import org.opendatakit.aggregate.client.odktables.TableDefinitionClient; import org.opendatakit.aggregate.client.odktables.TableEntryClient; -import org.opendatakit.aggregate.client.odktables.TableTypeClient; import org.opendatakit.aggregate.client.widgets.AggregateButton; import org.opendatakit.aggregate.client.widgets.ClosePopupButton; import org.opendatakit.aggregate.client.widgets.OdkTablesAddTableButton; import org.opendatakit.aggregate.client.widgets.OdkTablesTableIdBox; -import org.opendatakit.aggregate.client.widgets.OdkTablesTableNameBox; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; @@ -48,8 +46,6 @@ public class OdkTablesAddNewTablePopup extends AbstractPopupBase { // textbox for the tableid private OdkTablesTableIdBox idBox; - // the textbox for the display name - private OdkTablesTableNameBox displayNameBox; public OdkTablesAddNewTablePopup() { super(); @@ -58,7 +54,6 @@ public OdkTablesAddNewTablePopup() { addTableButton.addClickHandler(new ExecuteAdd()); idBox = new OdkTablesTableIdBox(this); - displayNameBox = new OdkTablesTableNameBox(this); FlexTable layout = new FlexTable(); @@ -66,10 +61,8 @@ public OdkTablesAddNewTablePopup() { layout.setWidget(0, 0, message); layout.setWidget(1, 0, new HTML("TableId:")); layout.setWidget(1, 1, idBox); - layout.setWidget(2, 0, new HTML("Display Name:")); - layout.setWidget(2, 1, displayNameBox); - layout.setWidget(3, 1, addTableButton); - layout.setWidget(3, 2, new ClosePopupButton(this)); + layout.setWidget(2, 1, addTableButton); + layout.setWidget(2, 2, new ClosePopupButton(this)); setWidget(layout); } @@ -80,15 +73,9 @@ private class ExecuteAdd implements ClickHandler { public void onClick(ClickEvent event) { String tableId = idBox.getValue(); - // displayName should be a JSON encoding of the data entered by the user - String displayName = displayNameBox.getValue(); - if (!(displayName != null && (displayName.startsWith("{") || displayName.startsWith("\""))) ) { - displayName = "\"" + displayName + "\""; - } ArrayList columns = new ArrayList(0); - tableDef = new TableDefinitionClient(tableId, columns, displayName, - TableTypeClient.DATA); + tableDef = new TableDefinitionClient(tableId, columns); // Set up the callback object. AsyncCallback callback = new AsyncCallback() { diff --git a/src/main/java/org/opendatakit/aggregate/client/table/OdkTablesTableList.java b/src/main/java/org/opendatakit/aggregate/client/table/OdkTablesTableList.java index 8680a609c6..dddbe8f5df 100644 --- a/src/main/java/org/opendatakit/aggregate/client/table/OdkTablesTableList.java +++ b/src/main/java/org/opendatakit/aggregate/client/table/OdkTablesTableList.java @@ -39,8 +39,8 @@ public class OdkTablesTableList extends FlexTable { // This will be the column with the table name - private static final int TABLE_NAME_COLUMN = 1; - private static final String TABLE_NAME_HEADING = "Table Name"; + private static final int TABLE_ID_COLUMN = 1; + private static final String TABLE_ID_HEADING = "Table Id"; private static final int DELETE_BUTTON_COLUMN = 0; private static final String DELETE_BUTTON_HEADING = "Delete"; @@ -71,30 +71,29 @@ public void updateTableList(ArrayList tables) { removeAllRows(); // now create the table headers - setText(0, TABLE_NAME_COLUMN, TABLE_NAME_HEADING); + setText(0, TABLE_ID_COLUMN, TABLE_ID_HEADING); addStyleName("dataTable"); getRowFormatter().addStyleName(0, "titleBar"); if (tables.size() == 0) { - setWidget(1, TABLE_NAME_COLUMN, new HTML(" There are no tables to display. ")); + setWidget(1, TABLE_ID_COLUMN, new HTML(" There are no tables to display. ")); } else { setText(0, DELETE_BUTTON_COLUMN, DELETE_BUTTON_HEADING); setText(0, VIEW_TABLE_BUTTON_COLUMN, VIEW_TABLE_BUTTON_HEADING); setText(0, TABLE_FILES_BUTTON_COLUMN, TABLE_FILE_BUTTON_HEADING); - setText(0, TABLE_NAME_COLUMN, TABLE_NAME_HEADING); + setText(0, TABLE_ID_COLUMN, TABLE_ID_HEADING); for (int i = 0; i < tables.size(); i++) { TableEntryClient table = tables.get(i); // this will maintain the row you're adding to, always +1 // because of the title row int j = i + 1; - setWidget(j, TABLE_NAME_COLUMN, new HTML("" + table.getDisplayName()+ "")); + setWidget(j, TABLE_ID_COLUMN, new HTML("" + table.getTableId()+ "")); setWidget(j, DELETE_BUTTON_COLUMN, new OdkTablesDeleteTableButton( this, table.getTableId())); setWidget(j, VIEW_TABLE_BUTTON_COLUMN, - new OdkTablesShowTableButton(this, table.getTableId(), - table.getDisplayName())); + new OdkTablesShowTableButton(this, table.getTableId())); setWidget(j, TABLE_FILES_BUTTON_COLUMN, new OdkTablesShowTableFilesButton(this, table.getTableId())); diff --git a/src/main/java/org/opendatakit/aggregate/client/widgets/OdkTablesShowTableButton.java b/src/main/java/org/opendatakit/aggregate/client/widgets/OdkTablesShowTableButton.java index 9cc581d720..66a1dbe31d 100644 --- a/src/main/java/org/opendatakit/aggregate/client/widgets/OdkTablesShowTableButton.java +++ b/src/main/java/org/opendatakit/aggregate/client/widgets/OdkTablesShowTableButton.java @@ -57,7 +57,7 @@ public class OdkTablesShowTableButton extends AggregateButton private String tableId; public OdkTablesShowTableButton(OdkTablesTableList parentTable, - String tableId, String tableName) { + String tableId) { super(BUTTON_TXT, TOOLTIP_TXT, HELP_BALLOON_TXT); this.parentTable = parentTable; this.tableId = tableId; diff --git a/src/main/java/org/opendatakit/aggregate/client/widgets/TableEntryClientListBox.java b/src/main/java/org/opendatakit/aggregate/client/widgets/TableEntryClientListBox.java index 00972a0dfe..395e135f28 100644 --- a/src/main/java/org/opendatakit/aggregate/client/widgets/TableEntryClientListBox.java +++ b/src/main/java/org/opendatakit/aggregate/client/widgets/TableEntryClientListBox.java @@ -34,22 +34,18 @@ public class TableEntryClientListBox extends AggregateListBox { private final ArrayList tables; public TableEntryClientListBox(ArrayList tables, - boolean multipleValueSelection, boolean onlyIncludeTableName, String tooltipText) { - this(tables, multipleValueSelection, onlyIncludeTableName, tooltipText, null); + boolean multipleValueSelection, String tooltipText) { + this(tables, multipleValueSelection, tooltipText, null); } public TableEntryClientListBox(ArrayList tables, - boolean multipleValueSelection, boolean onlyIncludeTableName, String tooltipText, + boolean multipleValueSelection, String tooltipText, String helpBalloonTxt) { super(tooltipText, multipleValueSelection, helpBalloonTxt); this.tables = tables; for (TableEntryClient table : tables) { - if (onlyIncludeTableName) { - addItem(table.getDisplayName(), table.getTableId()); - } else { // display both - addItem(table.getDisplayName() + "--" + table.getTableId(), table.getTableId()); - } + addItem(table.getTableId(), table.getTableId()); } } diff --git a/src/main/java/org/opendatakit/aggregate/odktables/PropertiesManager.java b/src/main/java/org/opendatakit/aggregate/odktables/PropertiesManager.java deleted file mode 100644 index 8d70466657..0000000000 --- a/src/main/java/org/opendatakit/aggregate/odktables/PropertiesManager.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright (C) 2012-2013 University of Washington - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.opendatakit.aggregate.odktables; - -import java.util.ArrayList; -import java.util.List; - -import org.apache.commons.lang3.Validate; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.opendatakit.aggregate.odktables.exception.ETagMismatchException; -import org.opendatakit.aggregate.odktables.exception.PermissionDeniedException; -import org.opendatakit.aggregate.odktables.relation.DbKeyValueStore; -import org.opendatakit.aggregate.odktables.relation.DbKeyValueStore.DbKeyValueStoreEntity; -import org.opendatakit.aggregate.odktables.relation.DbTableEntry; -import org.opendatakit.aggregate.odktables.relation.DbTableEntry.DbTableEntryEntity; -import org.opendatakit.aggregate.odktables.relation.EntityConverter; -import org.opendatakit.aggregate.odktables.relation.EntityCreator; -import org.opendatakit.aggregate.odktables.rest.entity.OdkTablesKeyValueStoreEntry; -import org.opendatakit.aggregate.odktables.rest.entity.TableProperties; -import org.opendatakit.aggregate.odktables.rest.entity.TableRole.TablePermission; -import org.opendatakit.aggregate.odktables.security.TablesUserPermissions; -import org.opendatakit.common.persistence.CommonFieldsBase; -import org.opendatakit.common.persistence.exception.ODKDatastoreException; -import org.opendatakit.common.persistence.exception.ODKEntityNotFoundException; -import org.opendatakit.common.persistence.exception.ODKTaskLockException; -import org.opendatakit.common.web.CallingContext; - -/** - * Manages getting and setting table name and metadata. - * - * @author the.dylan.price@gmail.com - * @author sudar.sam@gmail.com - * - */ -public class PropertiesManager { - private static final Log log = LogFactory.getLog(PropertiesManager.class); - - private CallingContext cc; - private TablesUserPermissions userPermissions; - private String appId; - private String tableId; - private EntityConverter converter; - - /** - * Construct a new PropertiesManager. - * - * @param tableId - * the unique identifier of the table - * @param cc - * the calling context - * @throws ODKEntityNotFoundException - * if no table with the given id exists - * @throws ODKDatastoreException - * if there is an internal error in the datastore - */ - public PropertiesManager(String appId, String tableId, TablesUserPermissions userPermissions, CallingContext cc) throws ODKEntityNotFoundException, - ODKDatastoreException { - Validate.notEmpty(appId); - Validate.notEmpty(tableId); - Validate.notNull(cc); - this.cc = cc; - this.userPermissions = userPermissions; - this.appId = appId; - this.tableId = tableId; - this.converter = new EntityConverter(); - } - - /** - * @return the appId that this PropertiesManager was constructed with. - */ - public String getAppId() { - return appId; - } - - /** - * @return the tableId that this PropertiesManager was constructed with. - */ - public String getTableId() { - return tableId; - } - - /** - * Retrieve the current table properties. - * - * @return the current table properties. - * @throws ODKDatastoreException - * @throws PermissionDeniedException - * @throws ODKTaskLockException - * @throws ETagMismatchException - */ - public TableProperties getProperties() throws ODKDatastoreException, PermissionDeniedException, ODKTaskLockException, ETagMismatchException { - userPermissions.checkPermission(appId, tableId, TablePermission.READ_PROPERTIES); - - String schemaETag = null; - String propertiesETag = null; - List kvsEntities = new ArrayList(); - LockTemplate lock = new LockTemplate(tableId, ODKTablesTaskLockType.TABLES_NON_PERMISSIONS_CHANGES, cc); - try { - lock.acquire(); - DbTableEntryEntity entry = DbTableEntry.getTableIdEntry(tableId, cc); - schemaETag = entry.getSchemaETag(); - if ( schemaETag != null ) { - propertiesETag = entry.getPropertiesETag(); - kvsEntities = DbKeyValueStore.getKVSEntries(tableId, propertiesETag, cc); - } - } finally { - lock.release(); - } - if ( schemaETag == null ) { - throw new ETagMismatchException(String.format( - "Unable to get table properties -- schema not defined for table with tableId %s", tableId)); - } - return converter.toTableProperties(kvsEntities, tableId, schemaETag, propertiesETag); - } - - /** - * Sets the table properties. - * - * @param tableProperties - * the table properties to set - * @return - * @throws ETagMismatchException - * if the given tableProperties' etag does not match the current - * properties etag. - * @throws ODKTaskLockException - * @throws ODKDatastoreException - * @throws PermissionDeniedException - */ - public TableProperties setProperties(TableProperties tableProperties) - throws ODKTaskLockException, ODKDatastoreException, ETagMismatchException, PermissionDeniedException { - - userPermissions.checkPermission(appId, tableId, TablePermission.WRITE_PROPERTIES); - String schemaETag = null; - // create new eTag - String propertiesETag = CommonFieldsBase.newUri(); - - // lock table - LockTemplate lock = new LockTemplate(tableId, ODKTablesTaskLockType.TABLES_NON_PERMISSIONS_CHANGES, cc); - try { - lock.acquire(); - - // refresh entry - DbTableEntryEntity entry = DbTableEntry.getTableIdEntry(tableId, cc); - - schemaETag = entry.getSchemaETag(); - if ( schemaETag == null ) { - throw new ETagMismatchException(String.format( - "Unable to set table properties -- schema not defined for table with tableId %s", tableId)); - } - if ( !schemaETag.equals(tableProperties.getSchemaETag()) ) { - throw new ETagMismatchException(String.format( - "Unable to set table properties -- schemaETag does not match for table with tableId %s", tableId)); - } - - String oldPropertiesETag = entry.getPropertiesETag(); - - // check etags - String currentETag = tableProperties.getPropertiesETag(); - if ((currentETag == null && oldPropertiesETag != null) || - (currentETag != null && oldPropertiesETag != null && !currentETag.equals(oldPropertiesETag))) { - throw new ETagMismatchException(String.format( - "%s does not match %s for properties for table with tableId %s", currentETag, - oldPropertiesETag, tableId)); - } - - // clean up any pending or stale state - TableManager.deleteVersionedTable(entry, false, cc); - - // write it as a pending eTag - entry.setPendingPropertiesETag(propertiesETag); - entry.put(cc); - - EntityCreator creator = new EntityCreator(); - - /** - * Enter the new properties in under the new propertiesETag. - * The old properties remain under the old propertiesETag - */ - log.info("setProperties: before kvs stuff in set properties"); - List kvsEntries = tableProperties.getKeyValueStoreEntries(); - OdkTablesKeyValueStoreEntry holderEntry = null; - List kvsEntities = new ArrayList(); - try { - for (OdkTablesKeyValueStoreEntry kvsEntry : kvsEntries) { - holderEntry = kvsEntry; - DbKeyValueStoreEntity kvsEntity = creator.newKeyValueStoreEntity(kvsEntry, - propertiesETag, cc); - kvsEntity.put(cc); - kvsEntities.add(kvsEntity); - } - } catch (Exception e) { - e.printStackTrace(); - // what is the deal. - log.error("setProperties (" + holderEntry.partition + ", " + holderEntry.aspect + ", " - + holderEntry.key + ") failed: " + e.toString()); - throw new ODKDatastoreException("Something went wrong in creating " + "key value " - + "store entries: " + e.toString(), e); - } - - // Wipe the existing kvsEntries. - // Caution! See javadoc of {@link clearAllEntries} and note that this is - // not done transactionally, so you could end up in a rough spot if your - // pursuant call to add all the new entities fails. - log.info("setProperties Made it past persist of pending kvsEntries"); - - entry.setStalePropertiesETag(entry.getPropertiesETag()); - entry.setPropertiesETag(entry.getPendingPropertiesETag()); - entry.setPendingPropertiesETag(null); - entry.put(cc); - - log.info("setProperties made it past persist of ETags"); - // set properties entity - TableManager.deleteVersionedTable(entry, false, cc); - log.info("setProperties cleaned up stale properties"); - - return converter.toTableProperties(kvsEntities, tableId, schemaETag, propertiesETag); - } finally { - lock.release(); - } - } -} diff --git a/src/main/java/org/opendatakit/aggregate/odktables/TableManager.java b/src/main/java/org/opendatakit/aggregate/odktables/TableManager.java index 21344c23ea..4758b1e0f8 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/TableManager.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/TableManager.java @@ -24,7 +24,6 @@ import org.opendatakit.aggregate.odktables.exception.TableAlreadyExistsException; import org.opendatakit.aggregate.odktables.relation.DbColumnDefinitions; import org.opendatakit.aggregate.odktables.relation.DbColumnDefinitions.DbColumnDefinitionsEntity; -import org.opendatakit.aggregate.odktables.relation.DbKeyValueStore; import org.opendatakit.aggregate.odktables.relation.DbLogTable; import org.opendatakit.aggregate.odktables.relation.DbTable; import org.opendatakit.aggregate.odktables.relation.DbTableAcl; @@ -408,15 +407,6 @@ public TableEntry createTable(String tableId, List columns) */ public static void deleteVersionedTable(DbTableEntryEntity tableEntry, boolean deleteCurrent, CallingContext cc) throws ODKDatastoreException, ODKEntityPersistException, ODKOverQuotaException { - // delete any stale properties - if ( tableEntry.getStalePropertiesETag() != null ) { - // delete entries - DbKeyValueStore.clearAllEntries(tableEntry.getId(), tableEntry.getStalePropertiesETag(), cc); - // record deletion success - tableEntry.setStalePropertiesETag(null); - tableEntry.put(cc); - } - // delete stale schema if ( tableEntry.getStaleSchemaETag() != null ) { // get the column schema @@ -472,23 +462,19 @@ public static void deleteVersionedTable(DbTableEntryEntity tableEntry, boolean d } // NOW: remove any pending schema or property changes - tableEntry.setStalePropertiesETag(tableEntry.getPendingPropertiesETag()); - tableEntry.setPendingPropertiesETag(null); tableEntry.setStaleSchemaETag(tableEntry.getPendingSchemaETag()); tableEntry.setPendingSchemaETag(null); - if ( tableEntry.getStalePropertiesETag() != null || tableEntry.getStaleSchemaETag() != null ) { + if ( tableEntry.getStaleSchemaETag() != null ) { // move the pending properties and schema to stale tableEntry.put(cc); // delete them (tail-recursive) deleteVersionedTable(tableEntry, deleteCurrent, cc); } else if ( deleteCurrent ) { // in addition to deleting the non-current cruft, we also need to delete the current data. - tableEntry.setStalePropertiesETag(tableEntry.getPropertiesETag()); - tableEntry.setPropertiesETag(null); tableEntry.setStaleSchemaETag(tableEntry.getSchemaETag()); tableEntry.setSchemaETag(null); - if ( tableEntry.getStalePropertiesETag() != null || tableEntry.getStaleSchemaETag() != null ) { + if ( tableEntry.getStaleSchemaETag() != null ) { // what had been the current schema, properties and row data now needs to be deleted tableEntry.put(cc); deleteVersionedTable(tableEntry, deleteCurrent, cc); diff --git a/src/main/java/org/opendatakit/aggregate/odktables/api/FileService.java b/src/main/java/org/opendatakit/aggregate/odktables/api/FileService.java index 0c1058fef9..fb12c726a1 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/api/FileService.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/api/FileService.java @@ -20,6 +20,7 @@ import javax.servlet.http.HttpServletRequest; import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; @@ -68,11 +69,24 @@ public interface FileService { @GET @Path("{filePath:.*}") @GZIP - public Response getFile(@PathParam("appId") String appId, @PathParam("odkClientVersion") String odkClientVersion, @PathParam("filePath") List segments, @QueryParam(PARAM_AS_ATTACHMENT) String asAttachment) throws IOException; + public Response getFile(@PathParam("appId") String appId, @PathParam("odkClientVersion") String odkClientVersion, @PathParam("filePath") List segments, @QueryParam(PARAM_AS_ATTACHMENT) String asAttachment) throws IOException, ODKTaskLockException; @POST @Path("{filePath:.*}") @Consumes({MediaType.MEDIA_TYPE_WILDCARD}) public Response putFile(@Context HttpServletRequest req, @PathParam("appId") String appId, @PathParam("odkClientVersion") String odkClientVersion, @PathParam("filePath") List segments, @GZIP byte[] content) throws IOException, ODKTaskLockException; + /** + * Delete only works on full file paths -- you cannot specify a partial path or wildcard (*) path. + * + * @param appId + * @param odkClientVersion + * @param segments + * @return + * @throws IOException + * @throws ODKTaskLockException + */ + @DELETE + @Path("{filePath:.*}") + public Response deleteFile(@PathParam("appId") String appId, @PathParam("odkClientVersion") String odkClientVersion, @PathParam("filePath") List segments) throws IOException, ODKTaskLockException; } diff --git a/src/main/java/org/opendatakit/aggregate/odktables/api/PropertiesService.java b/src/main/java/org/opendatakit/aggregate/odktables/api/PropertiesService.java deleted file mode 100644 index eceab50c4b..0000000000 --- a/src/main/java/org/opendatakit/aggregate/odktables/api/PropertiesService.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2012-2013 University of Washington - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.opendatakit.aggregate.odktables.api; - -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import org.jboss.resteasy.annotations.GZIP; -import org.opendatakit.aggregate.odktables.exception.ETagMismatchException; -import org.opendatakit.aggregate.odktables.exception.PermissionDeniedException; -import org.opendatakit.aggregate.odktables.rest.ApiConstants; -import org.opendatakit.aggregate.odktables.rest.entity.PropertiesResource; -import org.opendatakit.aggregate.odktables.rest.entity.TableProperties; -import org.opendatakit.common.persistence.exception.ODKDatastoreException; -import org.opendatakit.common.persistence.exception.ODKTaskLockException; - -@Produces({MediaType.APPLICATION_JSON, ApiConstants.MEDIA_TEXT_XML_UTF8, ApiConstants.MEDIA_APPLICATION_XML_UTF8}) -public interface PropertiesService { - - /** - * - * @return {@link PropertiesResource} - * @throws ODKDatastoreException - * @throws PermissionDeniedException - * @throws ODKTaskLockException - * @throws ETagMismatchException - */ - @GET - @Path("") - @GZIP - public Response /*PropertiesResource*/ getProperties() throws ODKDatastoreException, PermissionDeniedException, ODKTaskLockException, ETagMismatchException; - - /** - * - * @param properties - * @return {@link PropertiesResource} - * @throws ODKDatastoreException - * @throws ODKTaskLockException - * @throws ETagMismatchException - * @throws PermissionDeniedException - */ - @PUT - @Path("") - @Consumes({MediaType.APPLICATION_JSON, ApiConstants.MEDIA_TEXT_XML_UTF8, ApiConstants.MEDIA_APPLICATION_XML_UTF8}) - @GZIP - public Response /*PropertiesResource*/ setProperties(@GZIP TableProperties properties) throws ODKDatastoreException, - ODKTaskLockException, ETagMismatchException, PermissionDeniedException; - -} diff --git a/src/main/java/org/opendatakit/aggregate/odktables/api/TableService.java b/src/main/java/org/opendatakit/aggregate/odktables/api/TableService.java index e0d7e00f2c..7249dc27c0 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/api/TableService.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/api/TableService.java @@ -114,17 +114,6 @@ public interface TableService { public Response /*TableDefinitionResource*/ getDefinition(@PathParam("appId") String appId, @PathParam("tableId") String tableId) throws ODKDatastoreException, PermissionDeniedException, ODKTaskLockException; - /** - * - * @param appId - * @param tableId - * @return {@link PropertiesService} for accessing table metadata - * @throws ODKDatastoreException - */ - @Path("{tableId}/properties") - public PropertiesService getProperties(@PathParam("appId") String appId, @PathParam("tableId") String tableId) - throws ODKDatastoreException; - /** * * @param appId diff --git a/src/main/java/org/opendatakit/aggregate/odktables/entity/UtilTransforms.java b/src/main/java/org/opendatakit/aggregate/odktables/entity/UtilTransforms.java index c7b47bc54f..68a85cc09d 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/entity/UtilTransforms.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/entity/UtilTransforms.java @@ -16,39 +16,29 @@ package org.opendatakit.aggregate.odktables.entity; -import java.util.ArrayList; import java.util.Date; -import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.opendatakit.aggregate.client.odktables.ColumnClient; -import org.opendatakit.aggregate.client.odktables.OdkTablesKeyValueStoreEntryClient; -import org.opendatakit.aggregate.client.odktables.PropertiesResourceClient; import org.opendatakit.aggregate.client.odktables.RowClient; import org.opendatakit.aggregate.client.odktables.RowResourceClient; import org.opendatakit.aggregate.client.odktables.ScopeClient; import org.opendatakit.aggregate.client.odktables.TableAclClient; import org.opendatakit.aggregate.client.odktables.TableAclResourceClient; import org.opendatakit.aggregate.client.odktables.TableEntryClient; -import org.opendatakit.aggregate.client.odktables.TablePropertiesClient; import org.opendatakit.aggregate.client.odktables.TableResourceClient; import org.opendatakit.aggregate.client.odktables.TableRoleClient; -import org.opendatakit.aggregate.client.odktables.TableTypeClient; import org.opendatakit.aggregate.odktables.rest.TableConstants; import org.opendatakit.aggregate.odktables.rest.entity.Column; -import org.opendatakit.aggregate.odktables.rest.entity.OdkTablesKeyValueStoreEntry; -import org.opendatakit.aggregate.odktables.rest.entity.PropertiesResource; import org.opendatakit.aggregate.odktables.rest.entity.Row; import org.opendatakit.aggregate.odktables.rest.entity.RowResource; import org.opendatakit.aggregate.odktables.rest.entity.Scope; import org.opendatakit.aggregate.odktables.rest.entity.TableAcl; import org.opendatakit.aggregate.odktables.rest.entity.TableAclResource; import org.opendatakit.aggregate.odktables.rest.entity.TableEntry; -import org.opendatakit.aggregate.odktables.rest.entity.TableProperties; import org.opendatakit.aggregate.odktables.rest.entity.TableResource; import org.opendatakit.aggregate.odktables.rest.entity.TableRole; -import org.opendatakit.aggregate.odktables.rest.entity.TableType; import org.opendatakit.common.utils.WebUtils; /** @@ -70,92 +60,6 @@ public static Column transform(ColumnClient client) { return transformedColumn; } - public static TableType transform(TableTypeClient clientType) { - TableType serverType = TableType.DATA; - switch (clientType) { - case DATA: - serverType = TableType.DATA; - break; - case SECURITY: - serverType = TableType.SECURITY; - break; - case SHORTCUT: - serverType = TableType.SHORTCUT; - break; - default: - log.error("unrecognized client table type type: " + clientType); - } - return serverType; - } - - /** - * Transform server-side {@link OdkTablesKeyValueStoreEntry} into - * {@link OdkTablesKeyValueStoreEntryClient}. - * - * @param server - * @return - */ - public static OdkTablesKeyValueStoreEntryClient transform(OdkTablesKeyValueStoreEntry server) { - OdkTablesKeyValueStoreEntryClient client = new OdkTablesKeyValueStoreEntryClient(); - client.tableId = server.tableId; - client.partition = server.partition; - client.aspect = server.aspect; - client.key = server.key; - client.type = server.type; - client.value = server.value; - return client; - } - - /** - * Convenience method. Identical to calling transform on individual entries - * and constructing up a list. - * - * @param serverEntries - * @return - */ - public static ArrayList transform( - List serverEntries) { - ArrayList clientEntries = new ArrayList(); - for (OdkTablesKeyValueStoreEntry serverEntry : serverEntries) { - clientEntries.add(transform(serverEntry)); - } - return clientEntries; - } - - /** - * Transform client-side {@link OdkTablesKeyValueStoreEntryClient} into - * {@link OdkTablesKeyValueStoreEntry}. - * - * @param clientEntry - * @return - */ - public static OdkTablesKeyValueStoreEntry transform(OdkTablesKeyValueStoreEntryClient clientEntry) { - OdkTablesKeyValueStoreEntry serverEntry = new OdkTablesKeyValueStoreEntry(); - serverEntry.tableId = clientEntry.tableId; - serverEntry.partition = clientEntry.partition; - serverEntry.aspect = clientEntry.aspect; - serverEntry.key = clientEntry.key; - serverEntry.type = clientEntry.type; - serverEntry.value = clientEntry.value; - return serverEntry; - } - - /** - * Convenience method. Identical to calling transform on individual entries - * and constructing up a list. - * - * @param serverEntries - * @return - */ - public static ArrayList transformToServerEntries( - List clientEntries) { - ArrayList serverEntries = new ArrayList(); - for (OdkTablesKeyValueStoreEntryClient clientEntry : clientEntries) { - serverEntries.add(transform(clientEntry)); - } - return serverEntries; - } - /** * Transform into the server-side Row. */ @@ -239,9 +143,9 @@ public static TableAcl transform(TableAclClient client) { /** * Transforms the object into client-side TableEntryClient object. */ - public static TableEntryClient transform(TableEntry serverEntry, String displayName) { - TableEntryClient clientEntry = new TableEntryClient(serverEntry.getTableId(), displayName, - serverEntry.getDataETag(), serverEntry.getPropertiesETag(), serverEntry.getSchemaETag()); + public static TableEntryClient transform(TableEntry serverEntry) { + TableEntryClient clientEntry = new TableEntryClient(serverEntry.getTableId(), + serverEntry.getDataETag(), serverEntry.getSchemaETag()); return clientEntry; } @@ -249,44 +153,19 @@ public static TableEntryClient transform(TableEntry serverEntry, String displayN * This method transforms the TableResource into a client-side * TableResourceClient object. */ - public static TableResourceClient transform(TableResource serverResource, String displayName) { + public static TableResourceClient transform(TableResource serverResource) { TableResourceClient clientResource = new TableResourceClient(new TableEntryClient( - serverResource.getTableId(), displayName, serverResource.getDataETag(), - serverResource.getPropertiesETag(), serverResource.getSchemaETag())); + serverResource.getTableId(), serverResource.getDataETag(), + serverResource.getSchemaETag())); clientResource.setAclUri(serverResource.getAclUri()); clientResource.setDataUri(serverResource.getDataUri()); clientResource.setInstanceFilesUri(serverResource.getInstanceFilesUri()); clientResource.setDiffUri(serverResource.getDiffUri()); - clientResource.setPropertiesUri(serverResource.getPropertiesUri()); clientResource.setSelfUri(serverResource.getSelfUri()); clientResource.setDefinitionUri(serverResource.getDefinitionUri()); return clientResource; } - public static PropertiesResourceClient transform(PropertiesResource serverResource) { - TablePropertiesClient tpc = new TablePropertiesClient(serverResource.getSchemaETag(), - serverResource.getPropertiesETag(), - serverResource.getTableId(), UtilTransforms.transform(serverResource - .getKeyValueStoreEntries())); - PropertiesResourceClient resourceClient = new PropertiesResourceClient(tpc); - resourceClient.setSelfUri(serverResource.getSelfUri()); - resourceClient.setTableUri(serverResource.getTableUri()); - return resourceClient; - } - - /** - * Transform the object into the client-side object. - */ - public static TablePropertiesClient transform(TableProperties serverProperties) { - ArrayList clientEntries = new ArrayList(); - for (OdkTablesKeyValueStoreEntry serverEntry : serverProperties.getKeyValueStoreEntries()) { - clientEntries.add(UtilTransforms.transform(serverEntry)); - } - TablePropertiesClient tpClient = new TablePropertiesClient(serverProperties.getSchemaETag(), - serverProperties.getPropertiesETag(), serverProperties.getTableId(), clientEntries); - return tpClient; - } - /** * Transform the row into a client-side Row. */ diff --git a/src/main/java/org/opendatakit/aggregate/odktables/entity/serialization/OdkTablesKeyValueManifestManager.java b/src/main/java/org/opendatakit/aggregate/odktables/entity/serialization/OdkTablesKeyValueManifestManager.java deleted file mode 100644 index 752269b4cc..0000000000 --- a/src/main/java/org/opendatakit/aggregate/odktables/entity/serialization/OdkTablesKeyValueManifestManager.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2012-2013 University of Washington - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.opendatakit.aggregate.odktables.entity.serialization; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import org.codehaus.jackson.JsonGenerationException; -import org.codehaus.jackson.map.JsonMappingException; -import org.codehaus.jackson.map.ObjectMapper; -import org.opendatakit.aggregate.odktables.rest.entity.OdkTablesKeyValueStoreEntry; -import org.opendatakit.aggregate.odktables.security.TablesUserPermissions; -import org.opendatakit.common.web.CallingContext; - -/** - * This class manages the creation of the entries in the manifest. It creates - * this manifest, and then turns it into a JSON string to give down to the - * phone. It will be a list of OdkTablesKeyValueStoreEntry objects. - * - * @author sudar.sam@gmail.com - * - */ -public class OdkTablesKeyValueManifestManager { - - private List entries; - - private ObjectMapper mapper; - - private String appId; - - private String tableId; - - private CallingContext cc; - - private TablesUserPermissions userPermissions; - - private String manifest = null; - - /** - * Get the manifest ready for a specific table. - */ - public OdkTablesKeyValueManifestManager(String appId, String tableId, TablesUserPermissions userPermissions, CallingContext cc) { - this.appId = appId; - this.tableId = tableId; - this.cc = cc; - this.userPermissions = userPermissions; - mapper = new ObjectMapper(); - entries = new ArrayList(); - } - - - /** - * A single add method for testing json serialization. - * - * @param newEntry - */ - public void addEntry(OdkTablesKeyValueStoreEntry newEntry) { - entries.add(newEntry); - } - - /** - * Convenience method for adding a list of entries. Equivalent to calling - * addEntry multiple times. Only for use in testing. - */ - public void addEntries(List newEntries) { - for (OdkTablesKeyValueStoreEntry entry : newEntries) { - addEntry(entry); - } - } - - /** - * Get manifest for testing. - * - * @throws IOException - * @throws JsonMappingException - * @throws JsonGenerationException - */ - public String getManifestForTesting() throws JsonGenerationException, JsonMappingException, - IOException { - return mapper.writeValueAsString(entries); - } - -} diff --git a/src/main/java/org/opendatakit/aggregate/odktables/impl/api/FileServiceImpl.java b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/FileServiceImpl.java index 7b3ed880dd..9cecf5fc51 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/impl/api/FileServiceImpl.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/FileServiceImpl.java @@ -22,6 +22,7 @@ import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; @@ -48,6 +49,7 @@ import org.opendatakit.aggregate.odktables.relation.DbTableFileInfo.DbTableFileInfoEntity; import org.opendatakit.aggregate.odktables.relation.DbTableFiles; import org.opendatakit.aggregate.odktables.relation.EntityCreator; +import org.opendatakit.aggregate.odktables.rest.entity.TableRole.TablePermission; import org.opendatakit.aggregate.odktables.security.TablesUserPermissions; import org.opendatakit.aggregate.odktables.security.TablesUserPermissionsImpl; import org.opendatakit.common.datamodel.BinaryContentManipulator.BlobSubmissionOutcome; @@ -89,7 +91,7 @@ public FileServiceImpl(@Context ServletContext sc, @Context HttpServletRequest r @GET @Path("{filePath:.*}") // because we want to get the whole path - public Response getFile(@PathParam("appId") String appId, @PathParam("odkClientVersion") String odkClientVersion, @PathParam("filePath") List segments, @QueryParam(PARAM_AS_ATTACHMENT) String asAttachment) throws IOException { + public Response getFile(@PathParam("appId") String appId, @PathParam("odkClientVersion") String odkClientVersion, @PathParam("filePath") List segments, @QueryParam(PARAM_AS_ATTACHMENT) String asAttachment) throws IOException, ODKTaskLockException { // First we need to get the table id from the path. We're // going to be assuming that you're passing the entire path of the file // under /sdcard/opendatakit/appId/ e.g., tables/tableid/the/rest/of/path. @@ -106,6 +108,12 @@ public Response getFile(@PathParam("appId") String appId, @PathParam("odkClientV String contentType; Long contentLength; try { + // DbTableFileInfo.NO_TABLE_ID -- means that we are working with app-level permissions + if ( !DbTableFileInfo.NO_TABLE_ID.equals(tableId) ) { + userPermissions.checkPermission(appId, tableId, TablePermission.READ_PROPERTIES); + } + // otherwise, it is an app-level file, and that is accessible to anyone with synchronize tables privileges + List entities = DbTableFileInfo.queryForEntity(odkClientVersion, tableId, wholePath, cc); if (entities.size() > 1) { Log log = LogFactory.getLog(DbTableFileInfo.class); @@ -131,6 +139,9 @@ public Response getFile(@PathParam("appId") String appId, @PathParam("odkClientV } catch (ODKDatastoreException e) { e.printStackTrace(); return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Unable to retrieve attachment and access attributes for: " + wholePath).build(); + } catch (PermissionDeniedException e) { + LOGGER.error(("ODKTables file upload permissions error: " + e.getMessage())); + return Response.status(Status.UNAUTHORIZED).entity("Permission denied").build(); } // And now prepare everything to be returned to the caller. if (fileBlob != null && contentType != null && contentLength != null && contentLength != 0L) { @@ -159,8 +170,22 @@ public Response putFile(@Context HttpServletRequest req, @PathParam("appId") Str String wholePath = constructPathFromSegments(segments); String contentType = req.getContentType(); try { - TablesUserPermissionsImpl userPermissions = new TablesUserPermissionsImpl(cc.getCurrentUser() - .getUriUser(), cc); + // DbTableFileInfo.NO_TABLE_ID -- means that we are working with app-level permissions + if ( !DbTableFileInfo.NO_TABLE_ID.equals(tableId) ) { + userPermissions.checkPermission(appId, tableId, TablePermission.WRITE_PROPERTIES); + } + + // 0) Delete anything that is already stored + + List entities = DbTableFileInfo.queryForEntity(odkClientVersion, tableId, wholePath, cc); + for ( DbTableFileInfoEntity entity : entities ) { + + String uri = entity.getId(); + DbTableFiles dbTableFiles = new DbTableFiles(cc); + BlobEntitySet blobEntitySet = dbTableFiles.getBlobEntitySet(uri, cc); + blobEntitySet.remove(cc); + entity.delete(cc); + } // We are going to store the file in two tables: 1) a user-friendly table // that relates an app and table id to the name of a file; 2) a table @@ -213,6 +238,43 @@ public Response putFile(@Context HttpServletRequest req, @PathParam("appId") Str } } + @Override + @DELETE + @Path("{filePath:.*}") + // because we want to get the whole path + public Response deleteFile(@PathParam("appId") String appId, @PathParam("odkClientVersion") String odkClientVersion, @PathParam("filePath") List segments) throws IOException, ODKTaskLockException { + if (segments.size() < 1) { + return Response.status(Status.BAD_REQUEST).entity(FileService.ERROR_MSG_INSUFFICIENT_PATH).build(); + } + String tableId = getTableIdFromPathSegments(segments); + String wholePath = constructPathFromSegments(segments); + try { + // DbTableFileInfo.NO_TABLE_ID -- means that we are working with app-level permissions + if ( !DbTableFileInfo.NO_TABLE_ID.equals(tableId) ) { + userPermissions.checkPermission(appId, tableId, TablePermission.WRITE_PROPERTIES); + } + + // if we find nothing, we are happy. + List entities = DbTableFileInfo.queryForEntity(odkClientVersion, tableId, wholePath, cc); + for ( DbTableFileInfoEntity entity : entities ) { + + String uri = entity.getId(); + DbTableFiles dbTableFiles = new DbTableFiles(cc); + BlobEntitySet blobEntitySet = dbTableFiles.getBlobEntitySet(uri, cc); + blobEntitySet.remove(cc); + entity.delete(cc); + } + + return Response.ok().build(); + } catch (ODKDatastoreException e) { + LOGGER.error(("ODKTables file delete persistence error: " + e.getMessage())); + return Response.status(Status.INTERNAL_SERVER_ERROR).entity(ErrorConsts.PERSISTENCE_LAYER_PROBLEM + "\n" + e.getMessage()).build(); + } catch (PermissionDeniedException e) { + LOGGER.error(("ODKTables file delete permissions error: " + e.getMessage())); + return Response.status(Status.UNAUTHORIZED).entity("Permission denied").build(); + } + } + /** * Construct the path for the file. This is the entire path excluding the app * id. diff --git a/src/main/java/org/opendatakit/aggregate/odktables/impl/api/InstanceFileServiceImpl.java b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/InstanceFileServiceImpl.java index 9de1201cd5..d182f333bd 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/impl/api/InstanceFileServiceImpl.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/InstanceFileServiceImpl.java @@ -136,7 +136,7 @@ public Response getManifestAll(@QueryParam(PARAM_AS_ATTACHMENT) String asAttachm // because we want to get the whole path public Response getManifest(@PathParam("filePath") List segments, @QueryParam(PARAM_AS_ATTACHMENT) String asAttachment) throws IOException { - if (segments.size() <= 1) { + if (segments.size() < 1) { return Response.status(Status.BAD_REQUEST).entity(FileService.ERROR_MSG_INSUFFICIENT_PATH) .build(); } diff --git a/src/main/java/org/opendatakit/aggregate/odktables/impl/api/PropertiesServiceImpl.java b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/PropertiesServiceImpl.java deleted file mode 100644 index bc85c9248f..0000000000 --- a/src/main/java/org/opendatakit/aggregate/odktables/impl/api/PropertiesServiceImpl.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2012-2013 University of Washington - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.opendatakit.aggregate.odktables.impl.api; - -import java.net.URI; - -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriBuilder; -import javax.ws.rs.core.UriInfo; - -import org.opendatakit.aggregate.odktables.PropertiesManager; -import org.opendatakit.aggregate.odktables.api.PropertiesService; -import org.opendatakit.aggregate.odktables.api.TableService; -import org.opendatakit.aggregate.odktables.exception.ETagMismatchException; -import org.opendatakit.aggregate.odktables.exception.PermissionDeniedException; -import org.opendatakit.aggregate.odktables.rest.entity.PropertiesResource; -import org.opendatakit.aggregate.odktables.rest.entity.TableProperties; -import org.opendatakit.aggregate.odktables.security.TablesUserPermissions; -import org.opendatakit.common.persistence.exception.ODKDatastoreException; -import org.opendatakit.common.persistence.exception.ODKEntityNotFoundException; -import org.opendatakit.common.persistence.exception.ODKTaskLockException; -import org.opendatakit.common.web.CallingContext; - -public class PropertiesServiceImpl implements PropertiesService { - - private PropertiesManager pm; - private UriInfo info; - - public PropertiesServiceImpl(String appId, String tableId, UriInfo info, TablesUserPermissions userPermissions, CallingContext cc) - throws ODKEntityNotFoundException, ODKDatastoreException { - this.pm = new PropertiesManager(appId, tableId, userPermissions, cc); - this.info = info; - } - - @Override - public Response getProperties() throws ODKDatastoreException, PermissionDeniedException, ODKTaskLockException, ETagMismatchException { - TableProperties properties = pm.getProperties(); - PropertiesResource resource = getResource(properties); - return Response.ok(resource).build(); - } - - @Override - public Response setProperties(TableProperties properties) throws ODKDatastoreException, - ETagMismatchException, ODKTaskLockException, PermissionDeniedException { - properties = pm.setProperties(properties); - PropertiesResource resource = getResource(properties); - return Response.ok(resource).build(); - } - - private PropertiesResource getResource(TableProperties properties) { - PropertiesResource propertiesResource = new PropertiesResource(properties); - - String appId = pm.getAppId(); - String tableId = pm.getTableId(); - UriBuilder ub = info.getBaseUriBuilder(); - ub.path(TableService.class); - URI self = ub.clone().path(TableService.class, "getProperties") - .path(PropertiesService.class, "getProperties").build(appId, tableId); - URI table = ub.clone().path(TableService.class, "getTable").build(appId, tableId); - - propertiesResource.setSelfUri(self.toASCIIString()); - propertiesResource.setTableUri(table.toASCIIString()); - - return propertiesResource; - } - -} diff --git a/src/main/java/org/opendatakit/aggregate/odktables/impl/api/TableServiceImpl.java b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/TableServiceImpl.java index 129f40c02c..cd744e6986 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/impl/api/TableServiceImpl.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/impl/api/TableServiceImpl.java @@ -36,12 +36,10 @@ import org.opendatakit.aggregate.odktables.api.DataService; import org.opendatakit.aggregate.odktables.api.DiffService; import org.opendatakit.aggregate.odktables.api.InstanceFileService; -import org.opendatakit.aggregate.odktables.api.PropertiesService; import org.opendatakit.aggregate.odktables.api.TableAclService; import org.opendatakit.aggregate.odktables.api.TableService; import org.opendatakit.aggregate.odktables.exception.PermissionDeniedException; import org.opendatakit.aggregate.odktables.exception.TableAlreadyExistsException; -import org.opendatakit.aggregate.odktables.relation.DbKeyValueStore; import org.opendatakit.aggregate.odktables.rest.entity.Column; import org.opendatakit.aggregate.odktables.rest.entity.TableDefinition; import org.opendatakit.aggregate.odktables.rest.entity.TableDefinitionResource; @@ -80,11 +78,6 @@ public Response getTables(@PathParam("appId") String appId) throws ODKDatastoreE ArrayList resources = new ArrayList(); for (TableEntry entry : entries) { TableResource resource = getResource(appId, entry); - if (entry.getPropertiesETag() != null) { - String displayName = DbKeyValueStore.getDisplayName(entry.getTableId(), - entry.getPropertiesETag(), cc); - resource.setDisplayName(displayName); - } resources.add(resource); } // TODO: add QueryResumePoint support @@ -129,12 +122,6 @@ public DataService getData(@PathParam("appId") String appId, @PathParam("tableId return service; } - @Override - public PropertiesService getProperties(@PathParam("appId") String appId, @PathParam("tableId") String tableId) throws ODKDatastoreException { - PropertiesService service = new PropertiesServiceImpl(appId, tableId, info, userPermissions, cc); - return service; - } - @Override public DiffService getDiff(@PathParam("appId") String appId, @PathParam("tableId") String tableId) throws ODKDatastoreException { DiffService service = new DiffServiceImpl(appId, tableId, info, userPermissions, cc); @@ -173,7 +160,6 @@ private TableResource getResource(String appId, TableEntry entry) { UriBuilder ub = info.getBaseUriBuilder(); ub.path(TableService.class); URI self = ub.clone().path(TableService.class, "getTable").build(appId, tableId); - URI properties = ub.clone().path(TableService.class, "getProperties").build(appId, tableId); URI data = ub.clone().path(TableService.class, "getData").build(appId, tableId); URI instanceFiles = ub.clone().path(TableService.class, "getInstanceFiles").build(appId, tableId); URI diff = ub.clone().path(TableService.class, "getDiff").build(appId, tableId); @@ -183,7 +169,6 @@ private TableResource getResource(String appId, TableEntry entry) { TableResource resource = new TableResource(entry); resource.setSelfUri(self.toASCIIString()); resource.setDefinitionUri(definition.toASCIIString()); - resource.setPropertiesUri(properties.toASCIIString()); resource.setDataUri(data.toASCIIString()); resource.setInstanceFilesUri(instanceFiles.toASCIIString()); resource.setDiffUri(diff.toASCIIString()); diff --git a/src/main/java/org/opendatakit/aggregate/odktables/importexport/CsvUtil.java b/src/main/java/org/opendatakit/aggregate/odktables/importexport/CsvUtil.java index 3b4404e89e..652d658f44 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/importexport/CsvUtil.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/importexport/CsvUtil.java @@ -31,6 +31,8 @@ import au.com.bytecode.opencsv.CSVReader; /** + * This is OBSOLETE and BROKEN!!!! See the one from the Android codebase. + * * Holds various things for importing and exporting tables through CSVs. *

* Modified from the same class on the phone. diff --git a/src/main/java/org/opendatakit/aggregate/odktables/relation/DbKeyValueStore.java b/src/main/java/org/opendatakit/aggregate/odktables/relation/DbKeyValueStore.java deleted file mode 100644 index b8c1a3f632..0000000000 --- a/src/main/java/org/opendatakit/aggregate/odktables/relation/DbKeyValueStore.java +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright (C) 2012-2013 University of Washington - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.opendatakit.aggregate.odktables.relation; - -import java.util.ArrayList; -import java.util.List; - -import org.opendatakit.aggregate.odktables.rest.KeyValueStoreConstants; -import org.opendatakit.common.ermodel.Entity; -import org.opendatakit.common.ermodel.Query; -import org.opendatakit.common.ermodel.Relation; -import org.opendatakit.common.persistence.DataField; -import org.opendatakit.common.persistence.DataField.DataType; -import org.opendatakit.common.persistence.Query.FilterOperation; -import org.opendatakit.common.persistence.exception.ODKDatastoreException; -import org.opendatakit.common.persistence.exception.ODKEntityPersistException; -import org.opendatakit.common.persistence.exception.ODKOverQuotaException; -import org.opendatakit.common.web.CallingContext; - -/** - * The relation in the datastore that maps to the Server KeyValueStore on the - * phone. - * - * @author sudar.sam@gmail.com - * - */ -public class DbKeyValueStore extends Relation { - - private DbKeyValueStore(String namespace, String tableName, List fields, - CallingContext cc) throws ODKDatastoreException { - super(namespace, tableName, fields, cc); - } - - // The name of the table/relation in the datastore. - private static final String RELATION_NAME = "KEY_VALUE_STORE2"; - - // Column names. - private static final DataField TABLE_ID = new DataField("TABLE_ID", DataType.STRING, false); - private static final DataField PROPERTIES_ETAG = new DataField("PROPERTIES_ETAG", - DataType.STRING, false); - private static final DataField PARTITION = new DataField("PARTITION", DataType.STRING, false); - private static final DataField ASPECT = new DataField("ASPECT", DataType.STRING, false); - private static final DataField KEY = new DataField("KEY", DataType.STRING, false); - private static final DataField TYPE = new DataField("TYPE", DataType.STRING, false); - // NOTE: 19200L is due to a limitation in MySQL - private static final DataField VALUE = new DataField("VALUE", DataType.STRING, true, 19200L); - - private static final List dataFields; - static { - dataFields = new ArrayList(); - dataFields.add(TABLE_ID); - dataFields.add(PROPERTIES_ETAG); - dataFields.add(PARTITION); - dataFields.add(ASPECT); - dataFields.add(KEY); - dataFields.add(TYPE); - dataFields.add(VALUE); - } - - public static class DbKeyValueStoreEntity { - Entity e; - - DbKeyValueStoreEntity(Entity e) { - this.e = e; - } - - // Primary Key - public String getId() { - return e.getId(); - } - - public void put(CallingContext cc) throws ODKEntityPersistException, ODKOverQuotaException { - e.put(cc); - } - - public void delete(CallingContext cc) throws ODKDatastoreException { - e.delete(cc); - } - - // Accessors - - public String getTableId() { - return e.getString(TABLE_ID); - } - - public void setTableId(String value) { - e.set(TABLE_ID, value); - } - - public String getPropertiesETag() { - return e.getString(PROPERTIES_ETAG); - } - - public void setPropertiesETag(String value) { - e.set(PROPERTIES_ETAG, value); - } - - public String getPartition() { - return e.getString(PARTITION); - } - - public void setPartition(String value) { - e.set(PARTITION, value); - } - - public String getAspect() { - return e.getString(ASPECT); - } - - public void setAspect(String value) { - e.set(ASPECT, value); - } - - public String getKey() { - return e.getString(KEY); - } - - public void setKey(String value) { - e.set(KEY, value); - } - - public String getType() { - return e.getString(TYPE); - } - - public void setType(String value) { - e.set(TYPE, value); - } - - public String getValue() { - return e.getString(VALUE); - } - - public void setValue(String value) { - e.set(VALUE, value); - } - - } - - private static DbKeyValueStore relation = null; - - public static synchronized final DbKeyValueStore getRelation(CallingContext cc) - throws ODKDatastoreException { - if (relation == null) { - relation = new DbKeyValueStore(RUtil.NAMESPACE, RELATION_NAME, dataFields, cc); - } - return relation; - } - - /** - * Create a new row in this relation. The row is not yet persisted. - * - * @param cc - * @return - * @throws ODKDatastoreException - */ - public static DbKeyValueStoreEntity createNewEntity(CallingContext cc) - throws ODKDatastoreException { - return new DbKeyValueStoreEntity(getRelation(cc).newEntity(cc)); - } - - /** - * Delete all the key value store entities for the given table. - *

- * NB: No logging is performed! Currently no notion of transactions, so if - * this method is called and a subsequent add of new entities fails, there - * will be no recourse to restore the state. - * - * @param tableId - * @param propertiesETag - * @param cc - * @throws ODKDatastoreException - */ - public static void clearAllEntries(String tableId, String propertiesETag, CallingContext cc) - throws ODKDatastoreException { - List kvsEntities = getKVSEntries(tableId, propertiesETag, cc); - for (DbKeyValueStoreEntity entity : kvsEntities) { - entity.delete(cc); - } - } - - /** - * Get a List of Entity objects representing all the entries in the key value - * store for the given table. - * - * @param tableId - * @param propertiesETag - * @param cc - * @return - * @throws ODKDatastoreException - */ - public static List getKVSEntries(String tableId, String propertiesETag, - CallingContext cc) throws ODKDatastoreException { - - Query query = getRelation(cc).query("DbKeyValueStore.getKVSEntries", cc); - query.addFilter(TABLE_ID, FilterOperation.EQUAL, tableId); - query.addFilter(PROPERTIES_ETAG, FilterOperation.EQUAL, propertiesETag); - - List list = query.execute(); - List results = new ArrayList(); - for (Entity e : list) { - results.add(new DbKeyValueStoreEntity(e)); - } - return results; - } - - /** - * Get the displayName of a given tableId - * - * @param tableId - * @param propertiesETag - * @param cc - * @return - * @throws ODKDatastoreException - */ - public static String getDisplayName(String tableId, String propertiesETag, CallingContext cc) - throws ODKDatastoreException { - - Query query = getRelation(cc).query("DbKeyValueStore.getDisplayName", cc); - query.addFilter(TABLE_ID, FilterOperation.EQUAL, tableId); - query.addFilter(PROPERTIES_ETAG, FilterOperation.EQUAL, propertiesETag); - query.addFilter(PARTITION, FilterOperation.EQUAL, KeyValueStoreConstants.PARTITION_TABLE); - query.addFilter(ASPECT, FilterOperation.EQUAL, KeyValueStoreConstants.ASPECT_DEFAULT); - query.addFilter(KEY, FilterOperation.EQUAL, KeyValueStoreConstants.TABLE_DISPLAY_NAME); - - List list = query.execute(); - if (list.size() != 1) { - return "Ill-defined"; - } else { - Entity e = list.get(0); - return e.getString(VALUE); - } - } -} diff --git a/src/main/java/org/opendatakit/aggregate/odktables/relation/DbTableEntry.java b/src/main/java/org/opendatakit/aggregate/odktables/relation/DbTableEntry.java index 49250dbee7..594e160b0c 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/relation/DbTableEntry.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/relation/DbTableEntry.java @@ -72,7 +72,7 @@ private DbTableEntry(String namespace, String tableName, List fields, super(namespace, tableName, fields, cc); } - private static final String RELATION_NAME = "TABLE_ENTRY3"; + private static final String RELATION_NAME = "TABLE_ENTRY4"; /** * changes to row values that are in-progress but not complete will be tagged @@ -87,29 +87,6 @@ private DbTableEntry(String namespace, String tableName, List fields, private static final DataField DATA_ETAG = new DataField("DATA_ETAG", DataType.STRING, true); // there is no STALE_DATA_ETAG, as we maintain a history of all changes to the data values - /** - * changes to table properties that are in-progress but not complete are tagged - * with the pending properties ETag value. - */ - private static final DataField PENDING_PROPERTIES_ETAG = new DataField("PENDING_PROPERTIES_ETAG", - DataType.STRING, true); - /** - * Upon partial completion of the change, the stale properties ETag gets the old properties ETag value - * and the properties ETag value gets the pending properties ETag value, and the pending properties - * ETag value is set to null. This signals that the pending properties change is complete, but that there - * are stale properties that need to be cleaned up (since we do not remember the history of properties - * changes). - */ - private static final DataField PROPERTIES_ETAG = new DataField("PROPERTIES_ETAG", - DataType.STRING, true); - /** - * If the stale properties ETag != null, then we need to scan for all of these - * records and delete them. Once everything is deleted, the stale properties ETag should be - * set to null. - */ - private static final DataField STALE_PROPERTIES_ETAG = new DataField("STALE_PROPERTIES_ETAG", - DataType.STRING, true); - /** * When asynchronous schema changes become supported, the task uri is saved in this field. */ @@ -156,9 +133,6 @@ private DbTableEntry(String namespace, String tableName, List fields, dataFields = new ArrayList(); dataFields.add(PENDING_DATA_ETAG); dataFields.add(DATA_ETAG); - dataFields.add(PENDING_PROPERTIES_ETAG); - dataFields.add(PROPERTIES_ETAG); - dataFields.add(STALE_PROPERTIES_ETAG); dataFields.add(URI_SCHEMA_TASK); dataFields.add(PENDING_SCHEMA_ETAG); dataFields.add(SCHEMA_ETAG); @@ -205,30 +179,6 @@ public void setDataETag(String value) { e.set(DATA_ETAG, value); } - public String getPendingPropertiesETag() { - return e.getString(PENDING_PROPERTIES_ETAG); - } - - public void setPendingPropertiesETag(String value) { - e.set(PENDING_PROPERTIES_ETAG, value); - } - - public String getPropertiesETag() { - return e.getString(PROPERTIES_ETAG); - } - - public void setPropertiesETag(String value) { - e.set(PROPERTIES_ETAG, value); - } - - public String getStalePropertiesETag() { - return e.getString(STALE_PROPERTIES_ETAG); - } - - public void setStalePropertiesETag(String value) { - e.set(STALE_PROPERTIES_ETAG, value); - } - public String getUriSchemaTask() { return e.getString(URI_SCHEMA_TASK); } diff --git a/src/main/java/org/opendatakit/aggregate/odktables/relation/EntityConverter.java b/src/main/java/org/opendatakit/aggregate/odktables/relation/EntityConverter.java index c68dd8978f..63631fbb96 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/relation/EntityConverter.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/relation/EntityConverter.java @@ -23,19 +23,16 @@ import org.apache.commons.lang3.Validate; import org.opendatakit.aggregate.odktables.relation.DbColumnDefinitions.DbColumnDefinitionsEntity; -import org.opendatakit.aggregate.odktables.relation.DbKeyValueStore.DbKeyValueStoreEntity; import org.opendatakit.aggregate.odktables.relation.DbTableAcl.DbTableAclEntity; import org.opendatakit.aggregate.odktables.relation.DbTableDefinitions.DbTableDefinitionsEntity; import org.opendatakit.aggregate.odktables.relation.DbTableEntry.DbTableEntryEntity; import org.opendatakit.aggregate.odktables.relation.DbTableFileInfo.DbTableFileInfoEntity; import org.opendatakit.aggregate.odktables.rest.entity.Column; -import org.opendatakit.aggregate.odktables.rest.entity.OdkTablesKeyValueStoreEntry; import org.opendatakit.aggregate.odktables.rest.entity.Row; import org.opendatakit.aggregate.odktables.rest.entity.Scope; import org.opendatakit.aggregate.odktables.rest.entity.TableAcl; import org.opendatakit.aggregate.odktables.rest.entity.TableDefinition; import org.opendatakit.aggregate.odktables.rest.entity.TableEntry; -import org.opendatakit.aggregate.odktables.rest.entity.TableProperties; import org.opendatakit.aggregate.odktables.rest.entity.TableRole; import org.opendatakit.common.ermodel.Entity; import org.opendatakit.common.persistence.DataField; @@ -58,9 +55,8 @@ public class EntityConverter { public TableEntry toTableEntry(DbTableEntryEntity entity) { String tableId = entity.getId(); String dataETag = entity.getDataETag(); - String propertiesETag = entity.getPropertiesETag(); String schemaETag = entity.getSchemaETag(); - TableEntry entry = new TableEntry(tableId, dataETag, propertiesETag, schemaETag); + TableEntry entry = new TableEntry(tableId, dataETag, schemaETag); return entry; } @@ -108,30 +104,6 @@ public ArrayList toColumns(List entities) { return columns; } - public TableProperties toTableProperties(List kvsEntities, String tableId, - String schemaETag, String propertiesETag) { - ArrayList kvsEntries = toOdkTablesKeyValueStoreEntry(kvsEntities); - TableProperties properties = new TableProperties(schemaETag, propertiesETag, tableId, kvsEntries); - return properties; - } - - public OdkTablesKeyValueStoreEntry toOdkTablesKeyValueStoreEntry(DbKeyValueStoreEntity entity) { - String tableId = entity.getTableId(); - String partition = entity.getPartition(); - String aspect = entity.getAspect(); - String key = entity.getKey(); - String type = entity.getType(); - String value = entity.getValue(); - OdkTablesKeyValueStoreEntry entry = new OdkTablesKeyValueStoreEntry(); - entry.tableId = tableId; - entry.partition = partition; - entry.aspect = aspect; - entry.key = key; - entry.type = type; - entry.value = value; - return entry; - } - /** * Return a TableDefinition based upon the {@link DbTableDefinitionsEntity} * parameter, which must have been generated from the @@ -149,15 +121,6 @@ public TableDefinition toTableDefinition(TableEntry entryEntity, return td; } - public ArrayList toOdkTablesKeyValueStoreEntry( - List kvsEntities) { - ArrayList kvsEntries = new ArrayList(); - for (DbKeyValueStoreEntity entity : kvsEntities) { - kvsEntries.add(toOdkTablesKeyValueStoreEntry(entity)); - } - return kvsEntries; - } - /** * Convert a {@link DbTableAcl} entity to a {@link TableAcl} */ diff --git a/src/main/java/org/opendatakit/aggregate/odktables/relation/EntityCreator.java b/src/main/java/org/opendatakit/aggregate/odktables/relation/EntityCreator.java index e4648d624d..373866bfcc 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/relation/EntityCreator.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/relation/EntityCreator.java @@ -26,17 +26,15 @@ import org.opendatakit.aggregate.odktables.Sequencer; import org.opendatakit.aggregate.odktables.exception.BadColumnNameException; import org.opendatakit.aggregate.odktables.relation.DbColumnDefinitions.DbColumnDefinitionsEntity; -import org.opendatakit.aggregate.odktables.relation.DbKeyValueStore.DbKeyValueStoreEntity; import org.opendatakit.aggregate.odktables.relation.DbTableAcl.DbTableAclEntity; import org.opendatakit.aggregate.odktables.relation.DbTableDefinitions.DbTableDefinitionsEntity; import org.opendatakit.aggregate.odktables.relation.DbTableEntry.DbTableEntryEntity; import org.opendatakit.aggregate.odktables.relation.DbTableFileInfo.DbTableFileInfoEntity; import org.opendatakit.aggregate.odktables.rest.TableConstants; import org.opendatakit.aggregate.odktables.rest.entity.Column; -import org.opendatakit.aggregate.odktables.rest.entity.OdkTablesKeyValueStoreEntry; import org.opendatakit.aggregate.odktables.rest.entity.Scope; import org.opendatakit.aggregate.odktables.rest.entity.TableRole; -import org.opendatakit.aggregate.odktables.security.TablesUserPermissionsImpl; +import org.opendatakit.aggregate.odktables.security.TablesUserPermissions; import org.opendatakit.common.ermodel.Entity; import org.opendatakit.common.persistence.CommonFieldsBase; import org.opendatakit.common.persistence.exception.ODKDatastoreException; @@ -131,7 +129,7 @@ public DbColumnDefinitionsEntity newColumnEntity(String tableId, String schemaET */ public DbTableFileInfoEntity newTableFileInfoEntity(String odkClientVersion, String tableId, String pathToFile, - TablesUserPermissionsImpl userPermissions, + TablesUserPermissions userPermissions, CallingContext cc) throws ODKDatastoreException { // first do some preliminary checks Validate.notEmpty(pathToFile); @@ -187,55 +185,6 @@ public DbTableDefinitionsEntity newTableDefinitionEntity(String tableId, String return definition; } - /** - * - * @param tableId - * @param partition - * @param aspect - * @param key - * @param type - * @param value - * @param cc - * @return - * @throws ODKDatastoreException - */ - public DbKeyValueStoreEntity newKeyValueStoreEntity(String tableId, String propertiesETag, - String partition, String aspect, String key, String type, String value, CallingContext cc) - throws ODKDatastoreException { - Validate.notEmpty(tableId); - Validate.notEmpty(propertiesETag); - Validate.notEmpty(partition); - Validate.notEmpty(aspect); - Validate.notEmpty(key); - Validate.notEmpty(type); - Validate.notNull(cc); - - DbKeyValueStoreEntity entry = DbKeyValueStore.createNewEntity(cc); - entry.setTableId(tableId); - entry.setPropertiesETag(propertiesETag); - entry.setPartition(partition); - entry.setAspect(aspect); - entry.setKey(key); - entry.setType(type); - entry.setValue(value); - return entry; - } - - public DbKeyValueStoreEntity newKeyValueStoreEntity(OdkTablesKeyValueStoreEntry entry, - String propertiesETag, CallingContext cc) throws ODKDatastoreException { - Validate.notNull(entry); - Validate.notEmpty(propertiesETag); - Validate.notNull(cc); - - String tableId = entry.tableId; - String partition = entry.partition; - String aspect = entry.aspect; - String key = entry.key; - String type = entry.type; - String value = entry.value; - return newKeyValueStoreEntity(tableId, propertiesETag, partition, aspect, key, type, value, cc); - } - /** * Create a new {@link DbTableAcl} entity. * diff --git a/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/OdkTablesFileManifestEntry.java b/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/OdkTablesFileManifestEntry.java index dd76afbae5..6ef064d54c 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/OdkTablesFileManifestEntry.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/OdkTablesFileManifestEntry.java @@ -23,8 +23,7 @@ * This represents information about a file so that a phone running ODKTables * will be able to check to see if it has the most recent version of the file, * and if not will be able to download the file. It is meant to be mostly a - * struct that is parsed into and recovered from JSON, and work in tandem with - * {@link OdkTablesKeyValueStoreEntry}. + * struct that is parsed into and recovered from JSON. * * @author sudar.sam@gmail.com * diff --git a/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/OdkTablesKeyValueStoreEntry.java b/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/OdkTablesKeyValueStoreEntry.java deleted file mode 100644 index ec9f8dd946..0000000000 --- a/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/OdkTablesKeyValueStoreEntry.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2012-2013 University of Washington - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.opendatakit.aggregate.odktables.rest.entity; - -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -/** - * This is a simple struct-like object that will hold the rows from the key - * value store. It is meant to be parsed into JSON objects to passed to the - * phone. So this can be thought of as information ODKTables on the phone needs - * to know about an entry in the key value store. - *

- * For a more in-depth explanation of all these fields, see the - * KeyValueStoreManager.java class in ODK Tables. - * - * @author sudar.sam@gmail.com - * - */ -@Root(strict = false) -public class OdkTablesKeyValueStoreEntry { - - /** - * The table id of the table to which this entry belongs. - */ - @Element(required = true) - public String tableId; - - /** - * The partition in the key value store to which the entry belongs. For an in - * depth example see KeyValueStoreManager.java in the ODK Tables project. - * Otherwise, just know that it is essentially the identifier of the class - * that is responsible for managing the entry. ListView.java would have (by - * convention) a partition name ListView. TableProperties and ColumnProperties - * are the exception, belonging simply to the partitions "Table" and "Column". - */ - @Element(required = false) - public String partition; - - /** - * The aspect is essentially the scope, or the instance of the partition, to - * which this key/entry belongs. For instance, a table-wide property would - * have the aspect "default". A column's aspect would be its element key (ie - * its unique column identifier for the table). A particular saved graph view - * might have the display name of that graph. - */ - @Element(required = false) - public String aspect; - - /** - * The key of this entry. This is important so that ODKTables knows what to do - * with this entry. Eg a key of "list" might mean that this entry is important - * to the list view of the table. - */ - @Element(required = false) - public String key; - - /** - * The type of this entry. This is important to taht ODKTables knows how to - * interpret the value of this entry. Eg type String means that the value - * holds a string. Type file means that the value is a JSON object holding a - * FileManifestEntry object with information relating to the version of the - * file and how to get it. - */ - @Element(required = false) - public String type; - - /** - * The actual value of this entry. If the type is String, this is a string. If - * it is a File, it is a FileManifestEntry JSON object. - */ - @Element(required = false) - public String value; - - @Override - public String toString() { - return "[tableId=" + tableId + ", partition=" + partition + ", aspect=" + aspect + ", key=" - + key + ", type=" + type + ", value=" + value + "]"; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((tableId == null) ? 0 : tableId.hashCode()); - result = prime * result + ((partition == null) ? 0 : partition.hashCode()); - result = prime * result + ((aspect == null) ? 0 : aspect.hashCode()); - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((type == null) ? 0 : type.hashCode()); - result = prime * result + ((value == null) ? 0 : value.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (obj == this) { - return true; - } - if (!(obj instanceof OdkTablesKeyValueStoreEntry)) { - return false; - } - OdkTablesKeyValueStoreEntry other = (OdkTablesKeyValueStoreEntry) obj; - return (tableId == null ? other.tableId == null : tableId.equals(other.tableId)) - && (partition == null ? other.partition == null : partition.equals(other.partition)) - && (aspect == null ? other.aspect == null : aspect.equals(other.aspect)) - && (key == null ? other.key == null : key.equals(other.key)) - && (type == null ? other.type == null : type.equals(other.type)) - && (value == null ? other.value == null : value.equals(other.value)); - } - -} diff --git a/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/PropertiesResource.java b/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/PropertiesResource.java deleted file mode 100644 index 513c0fe6d7..0000000000 --- a/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/PropertiesResource.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2012-2013 University of Washington - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.opendatakit.aggregate.odktables.rest.entity; - -import org.simpleframework.xml.Default; -import org.simpleframework.xml.DefaultType; -import org.simpleframework.xml.Root; - -@Root -@Default(DefaultType.FIELD) -public class PropertiesResource extends TableProperties { - - private String selfUri; - private String tableUri; - - @SuppressWarnings("unused") - private PropertiesResource() { - } - - public PropertiesResource(TableProperties tableProperties) { - super(); - setPropertiesETag(tableProperties.getPropertiesETag()); - setTableId(tableProperties.getTableId()); - setKeyValueStoreEntries(tableProperties.getKeyValueStoreEntries()); - } - - public String getSelfUri() { - return this.selfUri; - } - - public String getTableUri() { - return this.tableUri; - } - - public void setSelfUri(final String selfUri) { - this.selfUri = selfUri; - } - - public void setTableUri(final String tableUri) { - this.tableUri = tableUri; - } - - @Override - public boolean equals(final Object o) { - if (o == this) - return true; - if (!(o instanceof PropertiesResource)) - return false; - final PropertiesResource other = (PropertiesResource) o; - if (!other.canEqual((java.lang.Object) this)) - return false; - if (!super.equals(o)) - return false; - if (this.getSelfUri() == null ? other.getSelfUri() != null : !this.getSelfUri().equals( - (java.lang.Object) other.getSelfUri())) - return false; - if (this.getTableUri() == null ? other.getTableUri() != null : !this.getTableUri().equals( - (java.lang.Object) other.getTableUri())) - return false; - return true; - } - - public boolean canEqual(final Object other) { - return other instanceof PropertiesResource; - } - - @Override - public int hashCode() { - final int PRIME = 31; - int result = 1; - result = result * PRIME + super.hashCode(); - result = result * PRIME + (this.getSelfUri() == null ? 0 : this.getSelfUri().hashCode()); - result = result * PRIME + (this.getTableUri() == null ? 0 : this.getTableUri().hashCode()); - return result; - } - - public String toString() { - return "PropertiesResource(super=" + super.toString() + ", selfUri=" + this.getSelfUri() - + ", tableUri=" + this.getTableUri() + ")"; - } - -} diff --git a/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/TableEntry.java b/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/TableEntry.java index 2d90ea2072..1373975e7e 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/TableEntry.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/TableEntry.java @@ -41,20 +41,15 @@ public class TableEntry { @Element(required = false) private String dataETag; - @Element(required = false) - private String propertiesETag; - @Element(required = false) private String schemaETag; protected TableEntry() { } - public TableEntry(final String tableId, final String dataETag, final String propertiesETag, - final String schemaETag) { + public TableEntry(final String tableId, final String dataETag, final String schemaETag) { this.tableId = tableId; this.dataETag = dataETag; - this.propertiesETag = propertiesETag; this.schemaETag = schemaETag; } @@ -74,14 +69,6 @@ public void setDataETag(final String dataETag) { this.dataETag = dataETag; } - public String getPropertiesETag() { - return propertiesETag; - } - - public void setPropertiesETag(String propertiesETag) { - this.propertiesETag = propertiesETag; - } - public String getSchemaETag() { return schemaETag; } @@ -96,7 +83,6 @@ public int hashCode() { int result = 1; result = prime * result + ((tableId == null) ? 1 : tableId.hashCode()); result = prime * result + ((dataETag == null) ? 1 : dataETag.hashCode()); - result = prime * result + ((propertiesETag == null) ? 1 : propertiesETag.hashCode()); result = prime * result + ((schemaETag == null) ? 1 : schemaETag.hashCode()); return result; } @@ -115,14 +101,11 @@ public boolean equals(Object obj) { TableEntry other = (TableEntry) obj; return (tableId == null ? other.tableId == null : tableId.equals(other.tableId)) && (dataETag == null ? other.dataETag == null : dataETag.equals(other.dataETag)) - && (propertiesETag == null ? other.propertiesETag == null : propertiesETag - .equals(other.propertiesETag)) && (schemaETag == null ? other.schemaETag == null : schemaETag.equals(other.schemaETag)); } @Override public String toString() { - return "TableEntry [tableId=" + tableId + ", dataETag=" + dataETag + ", propertiesETag=" - + propertiesETag + ", schemaETag=" + schemaETag + "]"; + return "TableEntry [tableId=" + tableId + ", dataETag=" + dataETag + ", schemaETag=" + schemaETag + "]"; } } \ No newline at end of file diff --git a/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/TableProperties.java b/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/TableProperties.java deleted file mode 100644 index 52f2cad73e..0000000000 --- a/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/TableProperties.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (C) 2012-2013 University of Washington - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.opendatakit.aggregate.odktables.rest.entity; - -import java.util.ArrayList; - -import org.simpleframework.xml.Element; -import org.simpleframework.xml.ElementList; -import org.simpleframework.xml.Root; - -/** - * Per Dylan's thesis, a TableProperties object represents only the metadata. - * The structural layout of the table is stored in the (keep these fully - * qualified!) - * {@link org.opendatakit.aggregate.odktables.relation.DbTableDefinitions} and - * {@link org.opendatakit.aggregate.odktables.relation.DbColumnDefinitions} - * tables. The metadata stored in this {@link TableProperties} object consists - * of a list of key value store entries. - * - * @author dylan price? - * @author sudar.sam@gmail.com - * - */ -@Root(strict = false) -public class TableProperties { - - @Element(name = "schemaETag", required = false) - private String schemaETag; - - @Element(name = "propertiesETag", required = false) - private String propertiesETag; - - @Element(name = "tableId", required = true) - private String tableId; - - @ElementList(inline = true, required = false) - private ArrayList kvsEntries; - - protected TableProperties() { - } - - /** - * - * @param schemaETag - * @param propertiesETag - * @param tableKey - * the tableKey field from {@link DbTableDefinition} - * @param keyValueStoreEntries - */ - public TableProperties(String schemaETag, String propertiesETag, String tableId, - ArrayList keyValueStoreEntries) { - this.schemaETag = schemaETag; - this.propertiesETag = propertiesETag; - this.tableId = tableId; - this.kvsEntries = keyValueStoreEntries; - } - - public String getSchemaETag() { - return schemaETag; - } - - public void setSchemaETag(String schemaETag) { - this.schemaETag = schemaETag; - } - - public String getPropertiesETag() { - return propertiesETag; - } - - public void setPropertiesETag(String propertiesETag) { - this.propertiesETag = propertiesETag; - } - - public String getTableId() { - return tableId; - } - - public void setTableId(String tableId) { - this.tableId = tableId; - } - - public void setKeyValueStoreEntries(ArrayList kvsEntries) { - this.kvsEntries = kvsEntries; - } - - public ArrayList getKeyValueStoreEntries() { - return this.kvsEntries; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((schemaETag == null) ? 1 : schemaETag.hashCode()); - result = prime * result + ((propertiesETag == null) ? 1 : propertiesETag.hashCode()); - result = prime * result + ((tableId == null) ? 1 : tableId.hashCode()); - result = prime * result + ((kvsEntries == null) ? 1 : kvsEntries.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (obj == this) { - return true; - } - if (!(obj instanceof TableProperties)) { - return false; - } - TableProperties other = (TableProperties) obj; - return (schemaETag == null ? other.schemaETag == null : schemaETag - .equals(other.schemaETag)) - && (propertiesETag == null ? other.propertiesETag == null : propertiesETag - .equals(other.propertiesETag)) - && (tableId == null ? other.tableId == null : tableId.equals(other.tableId)) - && (kvsEntries == null ? other.kvsEntries == null : kvsEntries.equals(other.kvsEntries)); - } - - @Override - public String toString() { - return "TableProperties [schemaETag=" + schemaETag + ", propertiesETag=" + propertiesETag + ", tableId=" + tableId - + ", kvsEntries=" + this.kvsEntries.toString() + "]"; - } - -} diff --git a/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/TableResource.java b/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/TableResource.java index f99217c5fe..e2f68bfe8f 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/TableResource.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/TableResource.java @@ -31,9 +31,6 @@ public class TableResource extends TableEntry { @Element(required = true) private String definitionUri; - @Element(required = true) - private String propertiesUri; - @Element(required = true) private String dataUri; @@ -46,11 +43,8 @@ public class TableResource extends TableEntry { @Element(required = true) private String aclUri; - @Element(required = false) - private String displayName; - public TableResource(TableEntry entry) { - super(entry.getTableId(), entry.getDataETag(), entry.getPropertiesETag(), entry.getSchemaETag()); + super(entry.getTableId(), entry.getDataETag(), entry.getSchemaETag()); } @SuppressWarnings("unused") @@ -65,10 +59,6 @@ public String getDefinitionUri() { return this.definitionUri; } - public String getPropertiesUri() { - return this.propertiesUri; - } - public String getDataUri() { return this.dataUri; } @@ -85,10 +75,6 @@ public String getAclUri() { return this.aclUri; } - public String getDisplayName() { - return this.displayName; - } - public void setSelfUri(final String selfUri) { this.selfUri = selfUri; } @@ -97,10 +83,6 @@ public void setDefinitionUri(final String definitionUri) { this.definitionUri = definitionUri; } - public void setPropertiesUri(final String propertiesUri) { - this.propertiesUri = propertiesUri; - } - public void setDataUri(final String dataUri) { this.dataUri = dataUri; } @@ -117,10 +99,6 @@ public void setAclUri(final String aclUri) { this.aclUri = aclUri; } - public void setDisplayName(final String displayName) { - this.displayName = displayName; - } - /* * (non-Javadoc) * @@ -135,11 +113,6 @@ public boolean equals(Object obj) { if (!(obj instanceof TableResource)) return false; TableResource other = (TableResource) obj; - if (displayName == null) { - if (other.displayName != null) - return false; - } else if (!displayName.equals(other.displayName)) - return false; if (aclUri == null) { if (other.aclUri != null) return false; @@ -160,11 +133,6 @@ public boolean equals(Object obj) { return false; } else if (!diffUri.equals(other.diffUri)) return false; - if (propertiesUri == null) { - if (other.propertiesUri != null) - return false; - } else if (!propertiesUri.equals(other.propertiesUri)) - return false; if (selfUri == null) { if (other.selfUri != null) return false; @@ -193,12 +161,10 @@ public boolean canEqual(final Object other) { public int hashCode() { final int prime = 31; int result = super.hashCode(); - result = prime * result + ((displayName == null) ? 0 : displayName.hashCode()); result = prime * result + ((aclUri == null) ? 0 : aclUri.hashCode()); result = prime * result + ((dataUri == null) ? 0 : dataUri.hashCode()); result = prime * result + ((instanceFilesUri == null) ? 0 : instanceFilesUri.hashCode()); result = prime * result + ((diffUri == null) ? 0 : diffUri.hashCode()); - result = prime * result + ((propertiesUri == null) ? 0 : propertiesUri.hashCode()); result = prime * result + ((selfUri == null) ? 0 : selfUri.hashCode()); result = prime * result + ((definitionUri == null) ? 0 : definitionUri.hashCode()); return result; @@ -216,8 +182,6 @@ public String toString() { builder.append(selfUri); builder.append(", defintionUri="); builder.append(definitionUri); - builder.append(", propertiesUri="); - builder.append(propertiesUri); builder.append(", dataUri="); builder.append(dataUri); builder.append(", instanceFilesUri="); @@ -226,8 +190,6 @@ public String toString() { builder.append(diffUri); builder.append(", aclUri="); builder.append(aclUri); - builder.append(", displayName="); - builder.append(displayName); builder.append("]"); return builder.toString(); } diff --git a/src/main/java/org/opendatakit/aggregate/server/ServerOdkTablesUtil.java b/src/main/java/org/opendatakit/aggregate/server/ServerOdkTablesUtil.java index 6830255023..036bf25385 100644 --- a/src/main/java/org/opendatakit/aggregate/server/ServerOdkTablesUtil.java +++ b/src/main/java/org/opendatakit/aggregate/server/ServerOdkTablesUtil.java @@ -33,7 +33,6 @@ import org.opendatakit.aggregate.client.odktables.TableDefinitionClient; import org.opendatakit.aggregate.client.odktables.TableEntryClient; import org.opendatakit.aggregate.odktables.DataManager; -import org.opendatakit.aggregate.odktables.PropertiesManager; import org.opendatakit.aggregate.odktables.TableManager; import org.opendatakit.aggregate.odktables.entity.UtilTransforms; import org.opendatakit.aggregate.odktables.exception.BadColumnNameException; @@ -43,13 +42,9 @@ import org.opendatakit.aggregate.odktables.exception.TableAlreadyExistsException; import org.opendatakit.aggregate.odktables.relation.DbTableFileInfo; import org.opendatakit.aggregate.odktables.relation.DbTableFiles; -import org.opendatakit.aggregate.odktables.rest.KeyValueStoreConstants; import org.opendatakit.aggregate.odktables.rest.entity.Column; -import org.opendatakit.aggregate.odktables.rest.entity.OdkTablesKeyValueStoreEntry; import org.opendatakit.aggregate.odktables.rest.entity.Row; import org.opendatakit.aggregate.odktables.rest.entity.TableEntry; -import org.opendatakit.aggregate.odktables.rest.entity.TableProperties; -import org.opendatakit.aggregate.odktables.rest.entity.TableType; import org.opendatakit.aggregate.odktables.security.TablesUserPermissions; import org.opendatakit.common.persistence.client.exception.DatastoreFailureException; import org.opendatakit.common.persistence.exception.ODKDatastoreException; @@ -94,8 +89,6 @@ public static TableEntryClient createTable(String appId, String tableId, TableDe // altering all of the requisite fields. try { TableManager tm = new TableManager(appId, userPermissions, cc); - String displayName = definition.getDisplayName(); - TableType type = UtilTransforms.transform(definition.getType()); List columns = definition.getColumns(); List columnsServer = new ArrayList(); @@ -103,43 +96,7 @@ public static TableEntryClient createTable(String appId, String tableId, TableDe columnsServer.add(UtilTransforms.transform(column)); } TableEntry entry = tm.createTable(tableId, columnsServer); - PropertiesManager pm = new PropertiesManager(appId, tableId, userPermissions, cc); - TableProperties tableProperties = pm.getProperties(); - // TODO: - // (1) add displayName (displayName) - // - OdkTablesKeyValueStoreEntry tn; - tn = new OdkTablesKeyValueStoreEntry(); - tn.tableId = tableId; - tn.partition = KeyValueStoreConstants.PARTITION_TABLE; - tn.aspect = KeyValueStoreConstants.ASPECT_DEFAULT; - tn.key = KeyValueStoreConstants.TABLE_DISPLAY_NAME; - tn.type = "json"; - tn.value = displayName; - - boolean foundTN = false; - boolean changedTN = false; - ArrayList kvsEntries = tableProperties.getKeyValueStoreEntries(); - for ( OdkTablesKeyValueStoreEntry kvs : kvsEntries ) { - if ( kvs.key == tn.key && kvs.aspect == tn.aspect && kvs.partition == tn.partition ) { - foundTN = true; - if ( tn.type.equals(kvs.type) && displayName.equals(kvs.value) ) { - changedTN = false; - } else { - kvs.type = tn.type; - kvs.value = displayName; - changedTN = true; - } - } - } - if ( !foundTN ) { - kvsEntries.add(tn); - } - if ( !foundTN || changedTN ) { - tableProperties.setKeyValueStoreEntries(kvsEntries); - pm.setProperties(tableProperties); - } - TableEntryClient entryClient = UtilTransforms.transform(entry, displayName); + TableEntryClient entryClient = UtilTransforms.transform(entry); logger.info(String.format("tableId: %s, definition: %s", tableId, definition)); return entryClient; } catch (ODKDatastoreException e) { diff --git a/src/main/java/org/opendatakit/aggregate/server/ServerPropertiesServiceImpl.java b/src/main/java/org/opendatakit/aggregate/server/ServerPropertiesServiceImpl.java deleted file mode 100644 index 3df8047503..0000000000 --- a/src/main/java/org/opendatakit/aggregate/server/ServerPropertiesServiceImpl.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2012-2013 University of Washington - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.opendatakit.aggregate.server; - -import javax.servlet.http.HttpServletRequest; - -import org.opendatakit.aggregate.ContextFactory; -import org.opendatakit.aggregate.client.exception.ETagMismatchExceptionClient; -import org.opendatakit.aggregate.client.exception.PermissionDeniedExceptionClient; -import org.opendatakit.aggregate.client.exception.RequestFailureException; -import org.opendatakit.aggregate.client.odktables.ServerPropertiesService; -import org.opendatakit.aggregate.client.odktables.TablePropertiesClient; -import org.opendatakit.aggregate.odktables.PropertiesManager; -import org.opendatakit.aggregate.odktables.entity.UtilTransforms; -import org.opendatakit.aggregate.odktables.exception.ETagMismatchException; -import org.opendatakit.aggregate.odktables.exception.PermissionDeniedException; -import org.opendatakit.aggregate.odktables.rest.entity.TableProperties; -import org.opendatakit.aggregate.odktables.security.TablesUserPermissions; -import org.opendatakit.aggregate.odktables.security.TablesUserPermissionsImpl; -import org.opendatakit.common.persistence.client.exception.DatastoreFailureException; -import org.opendatakit.common.persistence.exception.ODKDatastoreException; -import org.opendatakit.common.persistence.exception.ODKTaskLockException; -import org.opendatakit.common.security.client.exception.AccessDeniedException; -import org.opendatakit.common.web.CallingContext; - -import com.google.gwt.user.server.rpc.RemoteServiceServlet; - -public class ServerPropertiesServiceImpl extends RemoteServiceServlet implements - ServerPropertiesService { - - /** - * - */ - private static final long serialVersionUID = -109897492777781438L; - - @Override - public TablePropertiesClient getProperties(String tableId) throws AccessDeniedException, - RequestFailureException, DatastoreFailureException, PermissionDeniedExceptionClient { - HttpServletRequest req = this.getThreadLocalRequest(); - CallingContext cc = ContextFactory.getCallingContext(this, req); - try { - TablesUserPermissions userPermissions = new TablesUserPermissionsImpl(cc.getCurrentUser() - .getUriUser(), cc); - String appId = ServerPreferencesProperties.getOdkTablesAppId(cc); - PropertiesManager pm = new PropertiesManager(appId, tableId, userPermissions, cc); - TableProperties properties = pm.getProperties(); - return UtilTransforms.transform(properties); - } catch (ODKDatastoreException e) { - e.printStackTrace(); - throw new DatastoreFailureException(e); - } catch (ETagMismatchException e) { - e.printStackTrace(); - throw new RequestFailureException(e); - } catch (ODKTaskLockException e) { - e.printStackTrace(); - throw new RequestFailureException(e); - } catch (PermissionDeniedException e) { - e.printStackTrace(); - throw new PermissionDeniedExceptionClient(e); - } - } - - @Override - public TablePropertiesClient setProperties(TablePropertiesClient properties, String tableId) - throws AccessDeniedException, RequestFailureException, DatastoreFailureException, - ETagMismatchExceptionClient, PermissionDeniedExceptionClient { - HttpServletRequest req = this.getThreadLocalRequest(); - CallingContext cc = ContextFactory.getCallingContext(this, req); - try { - TablesUserPermissions userPermissions = new TablesUserPermissionsImpl(cc.getCurrentUser() - .getUriUser(), cc); - String appId = ServerPreferencesProperties.getOdkTablesAppId(cc); - PropertiesManager pm = new PropertiesManager(appId, tableId, userPermissions, cc); - // this seems fishy. Originally was passing in the parameter of type - // TablePropertiesClient, but it isn't clear why I need to, when - // nothing is being explicitly set. Fixed by changing the type of - // the variable, but should be wary of this. - // first make the type TableProperties object - TableProperties tableProperties = new TableProperties(properties.getSchemaETag(), properties.getPropertiesETag(), - properties.getTableId(), UtilTransforms.transformToServerEntries(properties - .getKeyValueStoreEntries())); - tableProperties = pm.setProperties(tableProperties); - return UtilTransforms.transform(tableProperties); - } catch (ODKDatastoreException e) { - e.printStackTrace(); - throw new DatastoreFailureException(e); - } catch (PermissionDeniedException e) { - e.printStackTrace(); - throw new PermissionDeniedExceptionClient(e); - } catch (ETagMismatchException e) { - e.printStackTrace(); - throw new ETagMismatchExceptionClient(e); - } catch (ODKTaskLockException e) { - e.printStackTrace(); - throw new RequestFailureException(e); - } - } - -} diff --git a/src/main/java/org/opendatakit/aggregate/server/ServerTableServiceImpl.java b/src/main/java/org/opendatakit/aggregate/server/ServerTableServiceImpl.java index e5426ab29b..d6f3c86c65 100644 --- a/src/main/java/org/opendatakit/aggregate/server/ServerTableServiceImpl.java +++ b/src/main/java/org/opendatakit/aggregate/server/ServerTableServiceImpl.java @@ -36,7 +36,6 @@ import org.opendatakit.aggregate.odktables.entity.UtilTransforms; import org.opendatakit.aggregate.odktables.exception.ETagMismatchException; import org.opendatakit.aggregate.odktables.exception.PermissionDeniedException; -import org.opendatakit.aggregate.odktables.relation.DbKeyValueStore; import org.opendatakit.aggregate.odktables.rest.entity.TableEntry; import org.opendatakit.aggregate.odktables.security.TablesUserPermissions; import org.opendatakit.aggregate.odktables.security.TablesUserPermissionsImpl; @@ -80,16 +79,12 @@ public ArrayList getTables() throws AccessDeniedException, Req TableManager tm = new TableManager(appId, userPermissions, cc); List entries = tm.getTables(); for (TableEntry entry : entries) { - if (entry.getPropertiesETag() != null) { - String displayName = DbKeyValueStore.getDisplayName(entry.getTableId(), - entry.getPropertiesETag(), cc); - clientEntries.add(UtilTransforms.transform(entry, displayName)); - } + clientEntries.add(UtilTransforms.transform(entry)); } Collections.sort(clientEntries, new Comparator() { @Override public int compare(TableEntryClient o1, TableEntryClient o2) { - return o1.getDisplayName().compareToIgnoreCase(o2.getDisplayName()); + return o1.getTableId().compareToIgnoreCase(o2.getTableId()); } }); @@ -117,12 +112,10 @@ public TableEntryClient getTable(String tableId) throws AccessDeniedException, String appId = ServerPreferencesProperties.getOdkTablesAppId(cc); TableManager tm = new TableManager(appId, userPermissions, cc); TableEntry entry = tm.getTableNullSafe(tableId); - if (entry == null || entry.getPropertiesETag() == null) { + if (entry == null) { return null; } - String displayName = DbKeyValueStore.getDisplayName(entry.getTableId(), - entry.getPropertiesETag(), cc); - TableEntryClient resource = UtilTransforms.transform(entry, displayName); + TableEntryClient resource = UtilTransforms.transform(entry); return resource; } catch (ODKDatastoreException e) { e.printStackTrace(); diff --git a/src/test/java/org/opendatakit/aggregate/odktables/AuthFilterTest.java b/src/test/java/org/opendatakit/aggregate/odktables/AuthFilterTest.java index ef2a8f03f4..b72c3ba3c3 100644 --- a/src/test/java/org/opendatakit/aggregate/odktables/AuthFilterTest.java +++ b/src/test/java/org/opendatakit/aggregate/odktables/AuthFilterTest.java @@ -31,7 +31,6 @@ import org.opendatakit.aggregate.odktables.rest.entity.Scope; import org.opendatakit.aggregate.odktables.rest.entity.Scope.Type; import org.opendatakit.aggregate.odktables.rest.entity.TableEntry; -import org.opendatakit.aggregate.odktables.rest.entity.TableProperties; import org.opendatakit.aggregate.odktables.rest.entity.TableRole; import org.opendatakit.aggregate.odktables.rest.entity.TableRole.TablePermission; import org.opendatakit.aggregate.odktables.security.AuthFilter; @@ -102,9 +101,6 @@ public void setUp() throws Exception { this.tm = new TableManager(T.appId, userPermissions, cc); TableEntry te = tm.createTable(tableId, T.columns); - PropertiesManager pm = new PropertiesManager( T.appId, tableId, userPermissions, cc); - TableProperties tableProperties = new TableProperties(te.getSchemaETag(), T.propertiesETag, tableId, T.kvsEntries); - pm.setProperties(tableProperties); this.am = new TableAclManager(T.appId, tableId, userPermissions, cc); List scopes = Lists.newArrayList(); diff --git a/src/test/java/org/opendatakit/aggregate/odktables/DataManagerTest.java b/src/test/java/org/opendatakit/aggregate/odktables/DataManagerTest.java index 476897b3ba..b2c8baff30 100644 --- a/src/test/java/org/opendatakit/aggregate/odktables/DataManagerTest.java +++ b/src/test/java/org/opendatakit/aggregate/odktables/DataManagerTest.java @@ -38,7 +38,6 @@ import org.opendatakit.aggregate.odktables.rest.entity.Scope; import org.opendatakit.aggregate.odktables.rest.entity.Scope.Type; import org.opendatakit.aggregate.odktables.rest.entity.TableEntry; -import org.opendatakit.aggregate.odktables.rest.entity.TableProperties; import org.opendatakit.aggregate.odktables.rest.entity.TableRole.TablePermission; import org.opendatakit.aggregate.odktables.security.TablesUserPermissions; import org.opendatakit.common.persistence.CommonFieldsBase; @@ -105,9 +104,6 @@ public void setUp() throws Exception { this.tm = new TableManager(T.appId, userPermissions, cc); TableEntry te = tm.createTable(T.tableId, T.columns); - PropertiesManager pm = new PropertiesManager( T.appId, T.tableId, userPermissions, cc); - TableProperties tableProperties = new TableProperties(te.getSchemaETag(), null, T.tableId, T.kvsEntries); - pm.setProperties(tableProperties); this.dm = new DataManager(T.appId, T.tableId, userPermissions, cc); diff --git a/src/test/java/org/opendatakit/aggregate/odktables/PropertiesManagerTest.java b/src/test/java/org/opendatakit/aggregate/odktables/PropertiesManagerTest.java deleted file mode 100644 index 26e0c3611f..0000000000 --- a/src/test/java/org/opendatakit/aggregate/odktables/PropertiesManagerTest.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (C) 2013 University of Washington - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.opendatakit.aggregate.odktables; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; - -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.opendatakit.aggregate.odktables.exception.ETagMismatchException; -import org.opendatakit.aggregate.odktables.exception.PermissionDeniedException; -import org.opendatakit.aggregate.odktables.rest.entity.Scope; -import org.opendatakit.aggregate.odktables.rest.entity.TableEntry; -import org.opendatakit.aggregate.odktables.rest.entity.TableProperties; -import org.opendatakit.aggregate.odktables.rest.entity.TableRole.TablePermission; -import org.opendatakit.aggregate.odktables.security.TablesUserPermissions; -import org.opendatakit.common.persistence.exception.ODKDatastoreException; -import org.opendatakit.common.persistence.exception.ODKEntityNotFoundException; -import org.opendatakit.common.persistence.exception.ODKTaskLockException; -import org.opendatakit.common.web.CallingContext; -import org.opendatakit.common.web.TestContextFactory; - -public class PropertiesManagerTest { - private CallingContext cc; - private TablesUserPermissions userPermissions; - private String eSchemaTag; - private String ePropertiesTag; - private String tableId; - private TableManager tm; - private PropertiesManager pm; - - private class MockCurrentUserPermissions implements TablesUserPermissions { - - @Override - public String getOdkTablesUserId() { - return "myid"; - } - - @Override - public String getPhoneNumber() { - return null; - } - - @Override - public String getXBearerCode() { - return null; - } - - @Override - public void checkPermission(String appId, String tableId, TablePermission permission) - throws ODKDatastoreException, PermissionDeniedException { - return; - } - - @Override - public boolean hasPermission(String appId, String tableId, TablePermission permission) - throws ODKDatastoreException { - return true; - } - - @Override - public boolean hasFilterScope(String appId, String tableId, TablePermission permission, String rowId, Scope filterScope) { - return true; - } - - } - - @Before - public void setUp() throws Exception { - this.cc = TestContextFactory.getCallingContext(); - - userPermissions = new MockCurrentUserPermissions(); - - this.tableId = T.tableId; - this.tm = new TableManager(T.appId, userPermissions, cc); - - TableEntry te = tm.createTable(tableId, T.columns); - this.pm = new PropertiesManager( T.appId, tableId, userPermissions, cc); - TableProperties tableProperties = new TableProperties(te.getSchemaETag(), null, tableId, T.kvsEntries); - TableProperties tpNew = pm.setProperties(tableProperties); - this.eSchemaTag = tpNew.getSchemaETag(); - this.ePropertiesTag = tpNew.getPropertiesETag(); - } - - @After - public void tearDown() throws Exception { - try { - tm.deleteTable(tableId); - } catch (ODKEntityNotFoundException e) { - // ignore - } - } - - @Test - public void testGetTableProperties() throws ODKDatastoreException, PermissionDeniedException, ODKTaskLockException, ETagMismatchException { - TableProperties expected = new TableProperties(this.eSchemaTag, this.ePropertiesTag, - T.tableId, T.kvsEntries); - TableProperties actual = pm.getProperties(); - assertEquals(expected.getTableId(), actual.getTableId()); - Util.assertCollectionSameElements(expected.getKeyValueStoreEntries(), - actual.getKeyValueStoreEntries()); - } - - // TODO: fix this when tableId and tableKey get sorted out... - @Ignore - public void testSetTableName() throws ODKTaskLockException, ODKDatastoreException, - ETagMismatchException, PermissionDeniedException, ETagMismatchException { - TableProperties expected = pm.getProperties(); - expected.setTableId("a new name"); // don't see how this would work... - - doTestSetProperties(expected); - } - - @Test - public void testSetTableMetadata() throws ODKTaskLockException, ODKDatastoreException, - ETagMismatchException, PermissionDeniedException, ETagMismatchException { - TableProperties expected = pm.getProperties(); - expected.setKeyValueStoreEntries(T.kvsEntries); - - doTestSetProperties(expected); - } - - private void doTestSetProperties(TableProperties expected) - throws ETagMismatchException, ODKTaskLockException, - ODKDatastoreException, PermissionDeniedException, ETagMismatchException { - pm.setProperties(expected); - - TableProperties actual = pm.getProperties(); - - assertEquals(expected.getTableId(), actual.getTableId()); - Util.assertCollectionSameElements(expected.getKeyValueStoreEntries(), - actual.getKeyValueStoreEntries()); - } - - @Test - public void testSetTableNameChangesPropertiesModNum() throws ODKDatastoreException, - ODKTaskLockException, ETagMismatchException, PermissionDeniedException, ETagMismatchException { - TableProperties properties = pm.getProperties(); - properties.setTableId("a new table name"); // don't see how this would work - - doTestSetPropertiesChangesModNum(properties); - } - - @Test - public void testSetTableMetadataChangesPropertiesModNum() throws ODKTaskLockException, - ODKDatastoreException, ETagMismatchException, PermissionDeniedException, ETagMismatchException { - TableProperties properties = pm.getProperties(); - properties.setKeyValueStoreEntries(T.kvsEntries); - - doTestSetPropertiesChangesModNum(properties); - } - - private void doTestSetPropertiesChangesModNum(TableProperties properties) - throws ODKDatastoreException, ETagMismatchException, ODKTaskLockException, PermissionDeniedException { - String startingPropertiesETag = tm.getTable(tableId).getPropertiesETag(); - String startingPropertiesETagTwo = properties.getPropertiesETag(); - assertEquals(startingPropertiesETag, startingPropertiesETagTwo); - - properties = pm.setProperties(properties); - - String endingPropertiesETag = tm.getTable(tableId).getPropertiesETag(); - String endingPropertiesETagTwo = properties.getPropertiesETag(); - assertEquals(endingPropertiesETag, endingPropertiesETagTwo); - - assertFalse(startingPropertiesETag.equals(endingPropertiesETag)); - assertFalse(startingPropertiesETagTwo.equals(endingPropertiesETagTwo)); - } - - @Test(expected = ETagMismatchException.class) - public void testCantChangePropertiesWithOldETag() throws ODKDatastoreException, - ETagMismatchException, ODKTaskLockException, PermissionDeniedException, ETagMismatchException { - TableProperties properties = pm.getProperties(); - properties.setTableId("new name"); - pm.setProperties(properties); - properties.setTableId("new name 2"); - pm.setProperties(properties); - } -} diff --git a/src/test/java/org/opendatakit/aggregate/odktables/T.java b/src/test/java/org/opendatakit/aggregate/odktables/T.java index 2ad6fb2404..4268e0d054 100644 --- a/src/test/java/org/opendatakit/aggregate/odktables/T.java +++ b/src/test/java/org/opendatakit/aggregate/odktables/T.java @@ -22,68 +22,15 @@ import java.util.Map; import org.junit.Ignore; -import org.opendatakit.aggregate.odktables.rest.KeyValueStoreConstants; import org.opendatakit.aggregate.odktables.rest.SavepointTypeManipulator; import org.opendatakit.aggregate.odktables.rest.TableConstants; import org.opendatakit.aggregate.odktables.rest.entity.Column; -import org.opendatakit.aggregate.odktables.rest.entity.OdkTablesKeyValueStoreEntry; import org.opendatakit.aggregate.odktables.rest.entity.Row; import org.opendatakit.aggregate.odktables.rest.entity.Scope; @Ignore public class T { - public static class OdkTablesKeyValueStoreEntries { - public static final OdkTablesKeyValueStoreEntry entryOne; - public static final OdkTablesKeyValueStoreEntry entryTwo; - public static final OdkTablesKeyValueStoreEntry displayName; - - public static final String entryOnePartition = "tablePartition"; - public static final String entryOneAspect = "tableAspect"; - public static final String entryOneKey = "theKey"; - public static final String entryOneType = "object"; - public static final String entryOneValue = "objectValue"; - public static final String entryTwoPartition = "customPartition"; - public static final String entryTwoAspect = "customAspect"; - public static final String entryTwoKey = "laClave"; - public static final String entryTwoType = "string"; - public static final String entryTwoValue = "thereIsNoPassword"; - - public static final String tablePartition = KeyValueStoreConstants.PARTITION_TABLE; - public static final String tableAspect = KeyValueStoreConstants.ASPECT_DEFAULT; - - public static final String displayNameKey = KeyValueStoreConstants.TABLE_DISPLAY_NAME; - public static final String displayNameType = "object"; - public static final String displayNameValue = "\"People\""; - - static { - entryOne = new OdkTablesKeyValueStoreEntry(); - entryOne.tableId = T.tableId; - entryOne.partition = entryOnePartition; - entryOne.aspect = entryOneAspect; - entryOne.key = entryOneKey; - entryOne.type = entryOneType; - entryOne.value = entryOneValue; - - entryTwo = new OdkTablesKeyValueStoreEntry(); - entryTwo.tableId = T.tableId; - entryTwo.partition = entryTwoPartition; - entryTwo.aspect = entryTwoAspect; - entryTwo.key = entryTwoKey; - entryTwo.type = entryTwoType; - entryTwo.value = entryTwoValue; - - displayName = new OdkTablesKeyValueStoreEntry(); - displayName.tableId = T.tableId; - displayName.partition = tablePartition; - displayName.aspect = tableAspect; - displayName.key = displayNameKey; - displayName.type = displayNameType; - displayName.value = displayNameValue; - - } - } - public static class Columns { public static final String name = "name"; @@ -160,9 +107,6 @@ public String getWeight() { public static final String appId = "app1"; public static final String tableId = "people"; public static final String tableMetadata = null; - public static final String propertiesETag = "propertiesETag"; - - public static final ArrayList kvsEntries = new ArrayList(); public static final List columns = new ArrayList(); public static final List columns2 = new ArrayList(); @@ -170,9 +114,6 @@ public String getWeight() { public static final List rows = new ArrayList(); static { - kvsEntries.add(T.OdkTablesKeyValueStoreEntries.entryOne); - kvsEntries.add(T.OdkTablesKeyValueStoreEntries.entryTwo); - kvsEntries.add(T.OdkTablesKeyValueStoreEntries.displayName); columns.add(T.Columns.column_name); columns.add(T.Columns.column_age); columns.add(T.Columns.column_weight); diff --git a/src/test/java/org/opendatakit/aggregate/odktables/TableAclManagerTest.java b/src/test/java/org/opendatakit/aggregate/odktables/TableAclManagerTest.java index 1e2be5a079..61a2b6698f 100644 --- a/src/test/java/org/opendatakit/aggregate/odktables/TableAclManagerTest.java +++ b/src/test/java/org/opendatakit/aggregate/odktables/TableAclManagerTest.java @@ -28,7 +28,6 @@ import org.opendatakit.aggregate.odktables.rest.entity.Scope; import org.opendatakit.aggregate.odktables.rest.entity.TableAcl; import org.opendatakit.aggregate.odktables.rest.entity.TableEntry; -import org.opendatakit.aggregate.odktables.rest.entity.TableProperties; import org.opendatakit.aggregate.odktables.rest.entity.TableRole; import org.opendatakit.aggregate.odktables.rest.entity.TableRole.TablePermission; import org.opendatakit.aggregate.odktables.security.TablesUserPermissions; @@ -94,9 +93,6 @@ public void setUp() throws Exception { this.tm = new TableManager(T.appId, userPermissions, cc); TableEntry te = tm.createTable(tableId, T.columns); - PropertiesManager pm = new PropertiesManager( T.appId, tableId, userPermissions, cc); - TableProperties tableProperties = new TableProperties(te.getSchemaETag(), null, tableId, T.kvsEntries); - tableProperties = pm.setProperties(tableProperties); this.scope = new Scope(Scope.Type.USER, T.user); this.role = TableRole.FILTERED_READER; diff --git a/src/test/java/org/opendatakit/aggregate/odktables/TableManagerTest.java b/src/test/java/org/opendatakit/aggregate/odktables/TableManagerTest.java index 011fdfd342..a573f2a322 100644 --- a/src/test/java/org/opendatakit/aggregate/odktables/TableManagerTest.java +++ b/src/test/java/org/opendatakit/aggregate/odktables/TableManagerTest.java @@ -17,7 +17,6 @@ package org.opendatakit.aggregate.odktables; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.util.ArrayList; @@ -33,7 +32,6 @@ import org.opendatakit.aggregate.odktables.rest.entity.Column; import org.opendatakit.aggregate.odktables.rest.entity.Scope; import org.opendatakit.aggregate.odktables.rest.entity.TableEntry; -import org.opendatakit.aggregate.odktables.rest.entity.TableProperties; import org.opendatakit.aggregate.odktables.rest.entity.TableRole; import org.opendatakit.aggregate.odktables.rest.entity.TableRole.TablePermission; import org.opendatakit.aggregate.odktables.security.TablesUserPermissions; @@ -57,8 +55,6 @@ public class TableManagerTest { private String tableId2; @SuppressWarnings("unused") private List columns; - @SuppressWarnings("unused") - private String tableProperties; private class MockCurrentUserPermissions implements TablesUserPermissions { @@ -105,7 +101,6 @@ public void setUp() throws Exception { this.tableId = T.tableId; this.tableId2 = T.tableId + "2"; this.columns = T.columns; - this.tableProperties = T.tableMetadata; } @After @@ -132,14 +127,9 @@ public void testGetTablesEmpty() throws ODKDatastoreException { public void testCreateTable() throws ODKDatastoreException, TableAlreadyExistsException, PermissionDeniedException, ODKTaskLockException, ETagMismatchException { TableEntry entry = tm.createTable(tableId, T.columns); - PropertiesManager pm = new PropertiesManager( T.appId, tableId, userPermissions, cc); - TableProperties tableProperties = new TableProperties(entry.getSchemaETag(), null, tableId, T.kvsEntries); - tableProperties = pm.setProperties(tableProperties); entry = tm.getTable(tableId); assertEquals(tableId, entry.getTableId()); - assertNotNull(entry.getPropertiesETag()); - assertEquals(tableProperties.getPropertiesETag(), entry.getPropertiesETag()); // data eTag is null when table is first created assertTrue(null == entry.getDataETag()); } @@ -168,19 +158,19 @@ public void testCreateTableAlreadyExistsTransitive() throws ODKDatastoreExceptio // @Test(expected = IllegalArgumentException.class) // public void testCreateTableNullTableId() throws ODKEntityPersistException, ODKDatastoreException, // TableAlreadyExistsException { -// tm.createTable(null, tableName, columns, tableProperties); +// tm.createTable(null, tableName, columns); // } // // @Test(expected = IllegalArgumentException.class) // public void testCreateTableNullTableName() throws ODKEntityPersistException, // ODKDatastoreException, TableAlreadyExistsException { -// tm.createTable(tableId, null, columns, tableProperties); +// tm.createTable(tableId, null, columns); // } // // @Test(expected = IllegalArgumentException.class) // public void testCreateTableNullColumns() throws ODKEntityPersistException, ODKDatastoreException, // TableAlreadyExistsException { -// tm.createTable(tableId, tableName, null, tableProperties); +// tm.createTable(tableId, tableName, null); // } @Test @@ -206,15 +196,9 @@ public void testGetTables() throws ODKEntityPersistException, ODKDatastoreExcept List expected = new ArrayList(); TableEntry entry = tm.createTable(tableId, T.columns); - PropertiesManager pm = new PropertiesManager( T.appId, tableId, userPermissions, cc); - TableProperties tableProperties = new TableProperties(entry.getSchemaETag(), null, tableId, T.kvsEntries); - pm.setProperties(tableProperties); TableEntry one = tm.getTable(tableId); TableEntry entry2 = tm.createTable(tableId2, T.columns); - PropertiesManager pm2 = new PropertiesManager( T.appId, tableId2, userPermissions, cc); - TableProperties tableProperties2 = new TableProperties(entry2.getSchemaETag(), null, tableId2, T.kvsEntries); - pm2.setProperties(tableProperties2); tm.createTable(tableId2, T.columns); TableEntry two = tm.getTable(tableId2); @@ -234,17 +218,9 @@ public void testGetTablesByScopes() throws ODKEntityNotFoundException, ODKDatast List expected = new ArrayList(); TableEntry entry = tm.createTable(tableId, T.columns); - PropertiesManager pm = new PropertiesManager( T.appId, tableId, userPermissions, cc); - TableProperties tableProperties = new TableProperties(entry.getSchemaETag(), T.propertiesETag, tableId, T.kvsEntries); - pm.setProperties(tableProperties); TableEntry one = tm.getTable(tableId); - @SuppressWarnings("unused") - TableEntry entry2 = tm.createTable(tableId2, T.columns); - PropertiesManager pm2 = new PropertiesManager( T.appId, tableId2, userPermissions, cc); - @SuppressWarnings("unused") - TableProperties tableProperties2 = new TableProperties(entry.getSchemaETag(), T.propertiesETag, tableId2, T.kvsEntries); - pm2.setProperties(tableProperties); + TableEntry entry2 = tm.createTable(tableId2, T.columns); tm.createTable(tableId2, T.columns); TableEntry two = tm.getTable(tableId2); @@ -265,9 +241,6 @@ public void testDeleteTable() throws ODKDatastoreException, ODKTaskLockException TableAlreadyExistsException, PermissionDeniedException, ETagMismatchException { TableEntry entry = tm.createTable(tableId, T.columns); - PropertiesManager pm = new PropertiesManager( T.appId, tableId, userPermissions, cc); - TableProperties tableProperties = new TableProperties(entry.getSchemaETag(), null, tableId, T.kvsEntries); - pm.setProperties(tableProperties); tm.deleteTable(tableId); tm.getTableNullSafe(tableId); } diff --git a/src/test/java/org/opendatakit/aggregate/odktables/entity/ManifestTest.java b/src/test/java/org/opendatakit/aggregate/odktables/entity/ManifestTest.java deleted file mode 100644 index 4722d14147..0000000000 --- a/src/test/java/org/opendatakit/aggregate/odktables/entity/ManifestTest.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (C) 2013 University of Washington - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.opendatakit.aggregate.odktables.entity; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import org.codehaus.jackson.JsonGenerationException; -import org.codehaus.jackson.JsonParseException; -import org.codehaus.jackson.map.JsonMappingException; -import org.codehaus.jackson.map.ObjectMapper; -import org.codehaus.jackson.type.TypeReference; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.opendatakit.aggregate.odktables.T; -import org.opendatakit.aggregate.odktables.entity.serialization.OdkTablesKeyValueManifestManager; -import org.opendatakit.aggregate.odktables.exception.PermissionDeniedException; -import org.opendatakit.aggregate.odktables.rest.entity.OdkTablesFileManifestEntry; -import org.opendatakit.aggregate.odktables.rest.entity.OdkTablesKeyValueStoreEntry; -import org.opendatakit.aggregate.odktables.rest.entity.Scope; -import org.opendatakit.aggregate.odktables.rest.entity.TableRole.TablePermission; -import org.opendatakit.aggregate.odktables.security.TablesUserPermissions; -import org.opendatakit.common.persistence.exception.ODKDatastoreException; -import org.opendatakit.common.web.CallingContext; -import org.opendatakit.common.web.TestContextFactory; - -/** - * Super basic test to see if the jackson library worked as expected. - * - * @author sudar.sam@gmail.com - * - */ -public class ManifestTest { - - private CallingContext cc; - private TablesUserPermissions userPermissions; - - private class MockCurrentUserPermissions implements TablesUserPermissions { - - @Override - public String getOdkTablesUserId() { - return "myid"; - } - - @Override - public String getPhoneNumber() { - return null; - } - - @Override - public String getXBearerCode() { - return null; - } - - @Override - public void checkPermission(String appId, String tableId, TablePermission permission) - throws ODKDatastoreException, PermissionDeniedException { - return; - } - - @Override - public boolean hasPermission(String appId, String tableId, TablePermission permission) - throws ODKDatastoreException { - return true; - } - - @Override - public boolean hasFilterScope(String appId, String tableId, TablePermission permission, String rowId, Scope filterScope) { - return true; - } - - } - - @Before - public void setUp() throws Exception { - this.cc = TestContextFactory.getCallingContext(); - userPermissions = new MockCurrentUserPermissions(); - } - - @After - public void tearDown() throws Exception { - } - - @Test - public void test() { - assertTrue(true); - } - - @Test - public void toJson() throws JsonGenerationException, JsonMappingException, IOException { - OdkTablesKeyValueStoreEntry entry = new OdkTablesKeyValueStoreEntry(); - entry.key = "list"; - entry.partition = null; - entry.aspect = "brother_of_skyrim_weapons"; - entry.tableId = "this-is-a-uuid"; - entry.value = "{greetings, I am a json string (no i'm not) }"; - entry.type = "file"; - - OdkTablesKeyValueStoreEntry entry2 = new OdkTablesKeyValueStoreEntry(); - entry2.key = "box"; - entry2.tableId = "this-is-a-uuid-TIMES-ONE-FREAKING-THOUSAND"; - entry2.partition = "foo"; - entry2.aspect = "sister_of_skyrim_weapons"; - entry2.value = "guess what's in the box..."; - entry2.type = "surprise"; - - List entryList = new ArrayList(); - entryList.add(entry); - entryList.add(entry2); - - OdkTablesKeyValueManifestManager manifest = new OdkTablesKeyValueManifestManager(T.appId, - "this-is-a-uuid", userPermissions, cc); - manifest.addEntries(entryList); - - assertEquals( - "[{\"tableId\":\"this-is-a-uuid\",\"partition\":null,\"aspect\":\"brother_of_skyrim_weapons\",\"key\":\"list\",\"type\":\"file\",\"value\":\"{greetings, I am a json string (no i'm not) }\"},{\"tableId\":\"this-is-a-uuid-TIMES-ONE-FREAKING-THOUSAND\",\"partition\":\"foo\",\"aspect\":\"sister_of_skyrim_weapons\",\"key\":\"box\",\"type\":\"surprise\",\"value\":\"guess what's in the box...\"}]", - manifest.getManifestForTesting()); - } - - /** - * This will hopefully take things from a manifest and convert them to - * objects. The manifest comes from a certain snapshot of the datastore. - * - * @throws IOException - * @throws JsonMappingException - * @throws JsonParseException - */ - @Test - public void getObjectsFromManifest() throws JsonParseException, JsonMappingException, IOException { - // String jsonManifest = - // "[{\"tableId\":\"b6ca94aa-68e2-4e8f-adf8-94ced5780fe3\",\"tableName\":\"skyrim weapons\",\"key\":\"box\",\"type\":\"file\",\"value\":\"{\\\"filename\":\"hellodatastore.txt\",\"md5hash\":\"md5:21f818fff26882fc9251a2a7c85e2cb0\",\"downloadUrl\":\"http://172.28.7.25:8888/tableFileDownload?blobKey=uuid%3A9ff2964c-266c-43f9-8d70-79adb281fc67&as_attachment=true\"}\"},{\"tableId\":\"b6ca94aa-68e2-4e8f-adf8-94ced5780fe3\",\"tableName\":\"skyrim weapons\",\"key\":\"list\",\"type\":\"file\",\"value\":\"{\"filename\":\"hellodatastore.txt\",\"md5hash\":\"md5:21f818fff26882fc9251a2a7c85e2cb0\",\"downloadUrl\":\"http://172.28.7.25:8888/tableFileDownload?blobKey=uuid%3A020c17df-22d1-4f9f-8990-393dd6a348cf&as_attachment=true\"}\"},{\"tableId\":\"b6ca94aa-68e2-4e8f-adf8-94ced5780fe3\",\"tableName\":\"skyrim weapons\",\"key\":\"list\",\"type\":\"file\",\"value\":\"{\"filename\":\"dylan_thesis.pdf\",\"md5hash\":\"md5:328c97d2ad43cd9e5de37a113bee3500\",\"downloadUrl\":\"http://172.28.7.25:8888/tableFileDownload?blobKey=uuid%3A7b6aa5ba-17af-4a13-adc8-2b7d507a9749&as_attachment=true\"}\"}]"; - // System.out.println(jsonManifest); - // String singleEntry = - // "{\"tableId\":\"b6ca94aa-68e2-4e8f-adf8-94ced5780fe3\",\"tableName\":\"skyrim weapons\",\"key\":\"box\",\"type\":\"file\",\"value\":\"{\"\"testesttest\"\"}\"\"}"; - // {\"filename\":\"hellodatastore.txt\",\"md5hash\":\"md5:21f818fff26882fc9251a2a7c85e2cb0\",\"downloadUrl\":\"http://172.28.7.25:8888/tableFileDownload?blobKey=uuid%3A9ff2964c-266c-43f9-8d70-79adb281fc67&as_attachment=true\"}\"}"; - String escapedManifest = "[{\"tableId\":\"b6ca94aa-68e2-4e8f-adf8-94ced5780fe3\",\"partition\":\"skyrim weapons\",\"aspect\":null,\"key\":\"detail\",\"type\":\"file\",\"value\":\"{\\\"filename\\\":\\\"emailPicsImport.py\\\",\\\"md5hash\\\":\\\"md5:333d7fa21642c1c8f1353d5f854631dd\\\",\\\"downloadUrl\\\":\\\"http://172.28.7.25:8888/tableFileDownload?blobKey=uuid%3A15e8556d-a4a9-456c-ab67-107e2e4dda5d&as_attachment=true\\\"}\"},{\"tableId\":\"b6ca94aa-68e2-4e8f-adf8-94ced5780fe3\",\"partition\":\"skyrim weapons\",\"aspect\":null,\"key\":\"box\",\"type\":\"file\",\"value\":\"{\\\"filename\\\":\\\"hellodatastore.txt\\\",\\\"md5hash\\\":\\\"md5:21f818fff26882fc9251a2a7c85e2cb0\\\",\\\"downloadUrl\\\":\\\"http://172.28.7.25:8888/tableFileDownload?blobKey=uuid%3A9ff2964c-266c-43f9-8d70-79adb281fc67&as_attachment=true\\\"}\"}]"; - ObjectMapper mapper = new ObjectMapper(); - TypeReference> typeRef = new TypeReference>() { - }; - ArrayList entries = mapper.readValue(escapedManifest, typeRef); - // OdkTablesKeyValueStoreEntry entry = mapper.readValue(singleEntry, - // OdkTablesKeyValueStoreEntry.class); - System.out.println("0"); - System.out.println(entries.get(0).key); - System.out.println(entries.get(0).tableId); - System.out.println(entries.get(0).type); - System.out.println(entries.get(0).value); - System.out.println("1"); - System.out.println(entries.get(1).key); - System.out.println(entries.get(1).tableId); - System.out.println(entries.get(1).type); - System.out.println(entries.get(1).value); - - OdkTablesFileManifestEntry fileEntry = mapper.readValue(entries.get(0).value, - OdkTablesFileManifestEntry.class); - System.out.println(fileEntry.downloadUrl); - System.out.println(fileEntry.filename); - System.out.println(fileEntry.md5hash); - } - -} diff --git a/src/test/java/org/opendatakit/aggregate/odktables/entity/SerializationTest.java b/src/test/java/org/opendatakit/aggregate/odktables/entity/SerializationTest.java index 3ed68fc3ce..438d8c35c2 100644 --- a/src/test/java/org/opendatakit/aggregate/odktables/entity/SerializationTest.java +++ b/src/test/java/org/opendatakit/aggregate/odktables/entity/SerializationTest.java @@ -92,7 +92,7 @@ public void testColumn() throws Exception { @Test public void testTableEntry() throws Exception { - TableEntry expected = new TableEntry("1", "data2", "property3", "schema4"); + TableEntry expected = new TableEntry("1", "data2", "schema4"); serializer.write(expected, writer); String xml = writer.toString(); System.out.println(xml); @@ -100,16 +100,6 @@ public void testTableEntry() throws Exception { assertEquals(expected, actual); } - @Test - public void testTableProperties() throws Exception { -// TableProperties expected = new TableProperties("0", "1", "2"); -// serializer.write(expected, writer); -// String xml = writer.toString(); -// System.out.println(xml); -// TableProperties actual = serializer.read(TableProperties.class, xml); -// assertEquals(expected, actual); - } - @Test public void testTableAcl() throws Exception { TableAcl expected = new TableAcl(TableRole.FILTERED_READER); @@ -121,19 +111,6 @@ public void testTableAcl() throws Exception { assertEquals(expected, actual); } - @Test - public void testTablePropertiesIsNotStrict() throws Exception { -// TableProperties expected = new TableProperties("0", "1", "2"); -// PropertiesResource resource = new PropertiesResource(expected); -// resource.setSelfUri("http://localhost:8080/odktables/tables/1/properties"); -// resource.setTableUri("http://localhost:8080/odktables/tables/1"); -// serializer.write(resource, writer); -// String xml = writer.toString(); -// System.out.println(xml); -// TableProperties actual = serializer.read(TableProperties.class, xml); -// assertEquals(expected, actual); - } - @Test public void testRowResource() throws Exception { Map values = T.Data.DYLAN.getValues(); @@ -161,12 +138,11 @@ public void testTableDefinition() throws Exception { @Test public void testTableResource() throws Exception { - TableEntry entry = new TableEntry("1", "data2", "property3", "schema4"); + TableEntry entry = new TableEntry("1", "data2", "schema4"); TableResource expected = new TableResource(entry); expected.setSelfUri("http://localhost:8080/odktables/tables/1"); expected.setDataUri("http://localhost:8080/odktables/tables/1/rows"); expected.setInstanceFilesUri("http://localhost:8080/odktables/tables/1/attachments"); - expected.setPropertiesUri("http://localhost:8080/odktables/tables/1/columns"); expected.setDefinitionUri("http://localhost:8080/odktables/tables/1/definition"); expected.setDiffUri("http://localhost:8080/odktables/tables/1/rows/diff"); expected.setAclUri("http://localhost:8080/odktables/tables/1/acl"); @@ -177,19 +153,6 @@ public void testTableResource() throws Exception { assertEquals(expected, actual); } - @Test - public void testPropertiesResource() throws Exception { -// TableProperties properties = new TableProperties("0", "1", "2"); -// PropertiesResource expected = new PropertiesResource(properties); -// expected.setSelfUri("http://localhost:8080/odktables/tables/1/properties"); -// expected.setTableUri("http://localhost:8080/odktables/tables/1"); -// serializer.write(expected, writer); -// String xml = writer.toString(); -// System.out.println(xml); -// PropertiesResource actual = serializer.read(PropertiesResource.class, xml); -// assertEquals(expected, actual); - } - @Test public void testTableAclResource() throws Exception { TableAcl acl = new TableAcl(TableRole.FILTERED_WRITER); From 1ad314db75e920b5e4f30aa1952ba907f6edcfb8 Mon Sep 17 00:00:00 2001 From: mitchellsundt Date: Thu, 1 May 2014 17:14:32 -0700 Subject: [PATCH 10/13] add the pending-files state to the SyncState --- .../org/opendatakit/aggregate/odktables/rest/SyncState.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/opendatakit/aggregate/odktables/rest/SyncState.java b/src/main/java/org/opendatakit/aggregate/odktables/rest/SyncState.java index bce2e0671d..05b94f943a 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/rest/SyncState.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/rest/SyncState.java @@ -46,5 +46,5 @@ * */ public enum SyncState { - rest, inserting, updating, deleting, conflicting; + rest, inserting, updating, deleting, conflicting, rest_pending_files; } From 62e0d0490550a92fe0b820ddcaddb5036902606b Mon Sep 17 00:00:00 2001 From: mitchellsundt Date: Fri, 2 May 2014 14:52:35 -0700 Subject: [PATCH 11/13] Update to safeguard against CSV import of file modified by Excel --- .../org/opendatakit/aggregate/odktables/rest/entity/Scope.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/Scope.java b/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/Scope.java index d19d4817d6..3bb87eff2e 100644 --- a/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/Scope.java +++ b/src/main/java/org/opendatakit/aggregate/odktables/rest/entity/Scope.java @@ -28,7 +28,7 @@ public class Scope { } public static Scope asScope(String filterType, String filterValue) { - if (filterType != null) { + if (filterType != null && filterType.length() != 0) { Scope.Type type = Scope.Type.valueOf(filterType); if (filterType.equals(Scope.Type.DEFAULT)) { return new Scope(Scope.Type.DEFAULT, null); From 12ac69111fb8e8ffe4164fb3fd179ce8b9578586 Mon Sep 17 00:00:00 2001 From: mitchellsundt Date: Mon, 5 May 2014 15:20:32 -0700 Subject: [PATCH 12/13] Fix UI update issue with new ODK Tables sub-tabs. --- .../aggregate/client/RefreshTimer.java | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/opendatakit/aggregate/client/RefreshTimer.java b/src/main/java/org/opendatakit/aggregate/client/RefreshTimer.java index 7295e01a6a..e6c244862c 100644 --- a/src/main/java/org/opendatakit/aggregate/client/RefreshTimer.java +++ b/src/main/java/org/opendatakit/aggregate/client/RefreshTimer.java @@ -25,10 +25,10 @@ * Background refresh timer that polls the server every 5 seconds for changes to * whichever tab is currently selected. If no UI interaction has happened for 5 * minutes, the refreshes stop. - * + * * @author wbrunette@gmail.com * @author mitchellsundt@gmail.com - * + * */ public class RefreshTimer extends Timer { @@ -44,22 +44,22 @@ public class RefreshTimer extends Timer { private static final int REFRESH_INTERVAL = 10000; // ms // private static final int REFRESH_INTERVAL = 100000; // ms - // MISC_REFRESH_MULTIPLIER is the multiplier to apply to - // the REFRESH_INTERVAL to slow down the rate at which - // an update occurs from the server. See code for what + // MISC_REFRESH_MULTIPLIER is the multiplier to apply to + // the REFRESH_INTERVAL to slow down the rate at which + // an update occurs from the server. See code for what // pages have this multiplier. private static final int MISC_REFRESH_MULTIPLIER = 3; - + // SUBMISSIONS_REFRESH_MULTIPLIER is the multiplier to apply // to the REFRESH_INTERVAL to slow down the rate at which // a fresh set of submissions is pulled from the server. private static final int SUBMISSIONS_REFRESH_MULTIPLIER = 7; - + // PREFERENCES_REFRESH_MULTIPLIER is the multiplier to apply - // to the REFRESH_INTERVAL to slow down the rate at which + // to the REFRESH_INTERVAL to slow down the rate at which // the preferences data is pulled from the server. private static final int PREFERENCES_REFRESH_MULTIPLIER = 6; - + // STALL_INTERVALS is the number of intervals of no UI // interaction after which the timer will be stopped. private static final int UI_STALL_INTERVALS = 18; // 3 min / 10 sec each @@ -71,10 +71,10 @@ public class RefreshTimer extends Timer { // intervalsElapsed counts the intervals since a UI interaction private int intervalsElapsed = 0; - + // intervalsCount counts the total number of intervals since page load or refreshNow() private int intervalsCount = 0; - + // isActive tracks the active/cancelled state of the timer // the GWT timer doesn't provide this information. private boolean isActive = false; @@ -173,7 +173,7 @@ public void run() { return; } intervalsElapsed++; - + if (currentSubTab != null) { SubTabInterface tabPanel = aggregateUI.getSubTab(currentSubTab); if (tabPanel == null) { @@ -181,7 +181,7 @@ public void run() { GWT.log("currentSubTab (" + currentSubTab.getHashString() + ")could not be found in RefreshTimer.run()"); } - + switch (currentSubTab) { case FORMS: case TABLES: @@ -228,7 +228,9 @@ public void run() { tabPanel.update(); } break; + case MANAGE_INSTANCE_FILES: case MANAGE_TABLE_ID_FILES: + case MANAGE_APP_LEVEL_FILES: if ((intervalsCount % MISC_REFRESH_MULTIPLIER) == 0) { GWT.log("manage files refresh"); tabPanel.update(); From 3eadd5b33835e91edcbeca40cc14ed0aeddd9a67 Mon Sep 17 00:00:00 2001 From: mitchellsundt Date: Mon, 5 May 2014 15:35:34 -0700 Subject: [PATCH 13/13] Clean up instance files listing webpage. --- .../table/OdkTablesViewInstanceFileInfo.java | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/opendatakit/aggregate/client/table/OdkTablesViewInstanceFileInfo.java b/src/main/java/org/opendatakit/aggregate/client/table/OdkTablesViewInstanceFileInfo.java index 320f3c51ab..7f51e2063f 100644 --- a/src/main/java/org/opendatakit/aggregate/client/table/OdkTablesViewInstanceFileInfo.java +++ b/src/main/java/org/opendatakit/aggregate/client/table/OdkTablesViewInstanceFileInfo.java @@ -53,16 +53,14 @@ public class OdkTablesViewInstanceFileInfo extends FlexTable { // this is the heading for the delete row button. private static final String DELETE_HEADING = "Delete"; - private static final int KEY_COLUMN = 1; - private static final String KEY_HEADING = "Key"; - private static final int TABLE_ID_COLUMN = 2; - private static final String TABLE_ID_HEADING = "TableId"; - private static final int FILENAME_COLUMN = 3; + private static final int INSTANCE_ID_COLUMN = 1; + private static final String INSTANCE_ID_HEADING = "Instance ID"; + private static final int FILENAME_COLUMN = 2; private static final String FILENAME_HEADING = "Filename"; - private static final int DOWNLOAD_COLUMN = 4; + private static final int DOWNLOAD_COLUMN = 3; private static final String DOWNLOAD_HEADING = "Download"; - private static final int numColumns = 5; + private static final int numColumns = 4; // this is just the tab that opened the table private AggregateSubTabBase basePanel; @@ -150,8 +148,7 @@ public void onSuccess(TableContentsForFilesClient tcc) { private void setColumnHeadings() { // create the table headers. setText(0, DELETE_COLUMN, DELETE_HEADING); - setText(0, KEY_COLUMN, KEY_HEADING); - setText(0, TABLE_ID_COLUMN, TABLE_ID_HEADING); + setText(0, INSTANCE_ID_COLUMN, INSTANCE_ID_HEADING); setText(0, FILENAME_COLUMN, FILENAME_HEADING); setText(0, DOWNLOAD_COLUMN, DOWNLOAD_HEADING); getRowFormatter().addStyleName(0, "titleBar"); @@ -180,8 +177,8 @@ private void setRows() { FileSummaryClient sum = fileSummaries.get(j); setWidget(currentRow, DELETE_COLUMN, new OdkTablesDeleteFileButton(this.basePanel, currentTable.getTableId(), sum.getId())); - setText(currentRow, KEY_COLUMN, sum.getId()); - setText(currentRow, TABLE_ID_COLUMN, sum.getTableId()); + String[] args = sum.getFilename().split("/"); + setText(currentRow, INSTANCE_ID_COLUMN, args[0]); setText(currentRow, FILENAME_COLUMN, sum.getFilename()); Widget downloadCol; if (sum.getDownloadUrl() != null) {