diff --git a/.travis.yml b/.travis.yml index 1e703899bbe..69fb0cd9adb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,6 +15,10 @@ language: java jdk: - oraclejdk8 +addons: + apt: + packages: + - expect before_install: - openssl aes-256-cbc -K $encrypted_37a4f399de75_key -iv $encrypted_37a4f399de75_iv -in service-account.json.enc -out service-account.json -d && export GOOGLE_APPLICATION_CREDENTIALS=$TRAVIS_BUILD_DIR/service-account.json GCLOUD_PROJECT=cloud-samples-tests diff --git a/appengine/datastore/indexes-exploding/pom.xml b/appengine/datastore/indexes-exploding/pom.xml new file mode 100644 index 00000000000..37a2a05154b --- /dev/null +++ b/appengine/datastore/indexes-exploding/pom.xml @@ -0,0 +1,91 @@ + + + 4.0.0 + war + 1.0-SNAPSHOT + com.example.appengine + appengine-datastore-indexes-exploding + + com.google.cloud + doc-samples + 1.0.0 + ../../.. + + + + com.google.appengine + appengine-api-1.0-sdk + ${appengine.sdk.version} + + + javax.servlet + servlet-api + jar + provided + + + + + junit + junit + 4.10 + test + + + org.mockito + mockito-all + 1.10.19 + test + + + com.google.appengine + appengine-testing + ${appengine.sdk.version} + test + + + com.google.appengine + appengine-api-stubs + ${appengine.sdk.version} + test + + + com.google.appengine + appengine-tools-sdk + ${appengine.sdk.version} + test + + + com.google.truth + truth + 0.28 + test + + + + + ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + + + com.google.appengine + appengine-maven-plugin + ${appengine.sdk.version} + + + + diff --git a/appengine/datastore/indexes-exploding/src/main/java/com/example/appengine/IndexesServlet.java b/appengine/datastore/indexes-exploding/src/main/java/com/example/appengine/IndexesServlet.java new file mode 100644 index 00000000000..dff4396b797 --- /dev/null +++ b/appengine/datastore/indexes-exploding/src/main/java/com/example/appengine/IndexesServlet.java @@ -0,0 +1,62 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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 com.example.appengine; + +import com.google.appengine.api.datastore.DatastoreService; +import com.google.appengine.api.datastore.DatastoreServiceFactory; +import com.google.appengine.api.datastore.Entity; +import com.google.appengine.api.datastore.FetchOptions; +import com.google.appengine.api.datastore.Query; +import com.google.appengine.api.datastore.Query.CompositeFilterOperator; +import com.google.appengine.api.datastore.Query.FilterOperator; +import com.google.appengine.api.datastore.Query.FilterPredicate; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.List; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * A servlet to demonstrate the use of Cloud Datastore indexes. + */ +public class IndexesServlet extends HttpServlet { + private final DatastoreService datastore; + + public IndexesServlet() { + datastore = DatastoreServiceFactory.getDatastoreService(); + } + + @Override + public void doGet(HttpServletRequest req, HttpServletResponse resp) + throws IOException, ServletException { + Query q = + new Query("Widget") + .setFilter( + CompositeFilterOperator.and( + new FilterPredicate("x", FilterOperator.EQUAL, 1), + new FilterPredicate("y", FilterOperator.EQUAL, "red"))) + .addSort("date", Query.SortDirection.ASCENDING); + List results = datastore.prepare(q).asList(FetchOptions.Builder.withDefaults()); + + PrintWriter out = resp.getWriter(); + out.printf("Got %d widgets.\n", results.size()); + } +} diff --git a/appengine/datastore/indexes-exploding/src/main/webapp/WEB-INF/appengine-web.xml b/appengine/datastore/indexes-exploding/src/main/webapp/WEB-INF/appengine-web.xml new file mode 100644 index 00000000000..e9d8b21cb8f --- /dev/null +++ b/appengine/datastore/indexes-exploding/src/main/webapp/WEB-INF/appengine-web.xml @@ -0,0 +1,21 @@ + + + + YOUR-PROJECT-ID + YOUR-VERSION-ID + true + diff --git a/appengine/datastore/indexes-exploding/src/main/webapp/WEB-INF/datastore-indexes.xml b/appengine/datastore/indexes-exploding/src/main/webapp/WEB-INF/datastore-indexes.xml new file mode 100644 index 00000000000..e0f85d3812d --- /dev/null +++ b/appengine/datastore/indexes-exploding/src/main/webapp/WEB-INF/datastore-indexes.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + diff --git a/appengine/datastore/indexes-exploding/src/main/webapp/WEB-INF/web.xml b/appengine/datastore/indexes-exploding/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..049fd7a05e7 --- /dev/null +++ b/appengine/datastore/indexes-exploding/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,39 @@ + + + + + indexes-servlet + com.example.appengine.IndexesServlet + + + indexes-servlet + / + + + + + profile + /* + + + CONFIDENTIAL + + + diff --git a/appengine/datastore/indexes-exploding/src/test/java/com/example/appengine/IndexesServletTest.java b/appengine/datastore/indexes-exploding/src/test/java/com/example/appengine/IndexesServletTest.java new file mode 100644 index 00000000000..451f2189d34 --- /dev/null +++ b/appengine/datastore/indexes-exploding/src/test/java/com/example/appengine/IndexesServletTest.java @@ -0,0 +1,104 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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 com.example.appengine; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.when; + +import com.google.appengine.api.datastore.DatastoreService; +import com.google.appengine.api.datastore.DatastoreServiceFactory; +import com.google.appengine.api.datastore.Entity; +import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig; +import com.google.appengine.tools.development.testing.LocalServiceTestHelper; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Arrays; +import java.util.Date; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * Unit tests for {@link IndexesServlet}. + */ +@RunWith(JUnit4.class) +public class IndexesServletTest { + + private final LocalServiceTestHelper helper = + new LocalServiceTestHelper( + // Set no eventual consistency, that way queries return all results. + // https://cloud.google.com/appengine/docs/java/tools/localunittesting#Java_Writing_High_Replication_Datastore_tests + new LocalDatastoreServiceTestConfig() + .setDefaultHighRepJobPolicyUnappliedJobPercentage(0)); + + @Mock private HttpServletRequest mockRequest; + @Mock private HttpServletResponse mockResponse; + private StringWriter responseWriter; + private IndexesServlet servletUnderTest; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + helper.setUp(); + + // Set up a fake HTTP response. + responseWriter = new StringWriter(); + when(mockResponse.getWriter()).thenReturn(new PrintWriter(responseWriter)); + + servletUnderTest = new IndexesServlet(); + } + + @After + public void tearDown() { + helper.tearDown(); + } + + @Test + public void doGet_emptyDatastore_writesNoWidgets() throws Exception { + servletUnderTest.doGet(mockRequest, mockResponse); + + assertThat(responseWriter.toString()) + .named("IndexesServlet response") + .isEqualTo("Got 0 widgets.\n"); + } + + @Test + public void doGet_repeatedPropertyEntities_writesWidgets() throws Exception { + DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); + // [START exploding_index_example_3] + Entity widget = new Entity("Widget"); + widget.setProperty("x", Arrays.asList(1, 2, 3, 4)); + widget.setProperty("y", Arrays.asList("red", "green", "blue")); + widget.setProperty("date", new Date()); + datastore.put(widget); + // [END exploding_index_example_3] + + servletUnderTest.doGet(mockRequest, mockResponse); + + assertThat(responseWriter.toString()) + .named("IndexesServlet response") + .isEqualTo("Got 1 widgets.\n"); + } +} diff --git a/appengine/datastore/indexes-perfect/pom.xml b/appengine/datastore/indexes-perfect/pom.xml new file mode 100644 index 00000000000..14fab365685 --- /dev/null +++ b/appengine/datastore/indexes-perfect/pom.xml @@ -0,0 +1,91 @@ + + + 4.0.0 + war + 1.0-SNAPSHOT + com.example.appengine + appengine-datastore-indexes-perfect + + com.google.cloud + doc-samples + 1.0.0 + ../../.. + + + + com.google.appengine + appengine-api-1.0-sdk + ${appengine.sdk.version} + + + javax.servlet + servlet-api + jar + provided + + + + + junit + junit + 4.10 + test + + + org.mockito + mockito-all + 1.10.19 + test + + + com.google.appengine + appengine-testing + ${appengine.sdk.version} + test + + + com.google.appengine + appengine-api-stubs + ${appengine.sdk.version} + test + + + com.google.appengine + appengine-tools-sdk + ${appengine.sdk.version} + test + + + com.google.truth + truth + 0.28 + test + + + + + ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + + + com.google.appengine + appengine-maven-plugin + ${appengine.sdk.version} + + + + diff --git a/appengine/datastore/indexes-perfect/src/main/java/com/example/appengine/IndexesServlet.java b/appengine/datastore/indexes-perfect/src/main/java/com/example/appengine/IndexesServlet.java new file mode 100644 index 00000000000..0dbca3ce307 --- /dev/null +++ b/appengine/datastore/indexes-perfect/src/main/java/com/example/appengine/IndexesServlet.java @@ -0,0 +1,98 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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 com.example.appengine; + +import com.google.appengine.api.datastore.DatastoreService; +import com.google.appengine.api.datastore.DatastoreServiceFactory; +import com.google.appengine.api.datastore.Entity; +import com.google.appengine.api.datastore.FetchOptions; +import com.google.appengine.api.datastore.Query; +import com.google.appengine.api.datastore.Query.CompositeFilterOperator; +import com.google.appengine.api.datastore.Query.FilterOperator; +import com.google.appengine.api.datastore.Query.FilterPredicate; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.List; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * A servlet to demonstrate the use of Cloud Datastore indexes. + */ +public class IndexesServlet extends HttpServlet { + private final DatastoreService datastore; + + public IndexesServlet() { + datastore = DatastoreServiceFactory.getDatastoreService(); + } + + @Override + public void doGet(HttpServletRequest req, HttpServletResponse resp) + throws IOException, ServletException { + PrintWriter out = resp.getWriter(); + // These queries should all work with the same index. + // [START queries_and_indexes_example_1] + Query q1 = + new Query("Person") + .setFilter( + CompositeFilterOperator.and( + new FilterPredicate("lastName", FilterOperator.EQUAL, "Smith"), + new FilterPredicate("height", FilterOperator.EQUAL, 72))) + .addSort("height", Query.SortDirection.DESCENDING); + // [END queries_and_indexes_example_1] + List r1 = datastore.prepare(q1).asList(FetchOptions.Builder.withDefaults()); + out.printf("Got %d results from query 1.\n", r1.size()); + + // [START queries_and_indexes_example_2] + Query q2 = + new Query("Person") + .setFilter( + CompositeFilterOperator.and( + new FilterPredicate("lastName", FilterOperator.EQUAL, "Jones"), + new FilterPredicate("height", FilterOperator.EQUAL, 63))) + .addSort("height", Query.SortDirection.DESCENDING); + // [END queries_and_indexes_example_2] + List r2 = datastore.prepare(q2).asList(FetchOptions.Builder.withDefaults()); + out.printf("Got %d results from query 2.\n", r2.size()); + + // [START queries_and_indexes_example_3] + Query q3 = + new Query("Person") + .setFilter( + CompositeFilterOperator.and( + new FilterPredicate("lastName", FilterOperator.EQUAL, "Friedkin"), + new FilterPredicate("firstName", FilterOperator.EQUAL, "Damian"))) + .addSort("height", Query.SortDirection.ASCENDING); + // [END queries_and_indexes_example_3] + List r3 = datastore.prepare(q3).asList(FetchOptions.Builder.withDefaults()); + out.printf("Got %d results from query 3.\n", r3.size()); + + // [START queries_and_indexes_example_4] + Query q4 = + new Query("Person") + .setFilter(new FilterPredicate("lastName", FilterOperator.EQUAL, "Blair")) + .addSort("firstName", Query.SortDirection.ASCENDING) + .addSort("height", Query.SortDirection.ASCENDING); + // [END queries_and_indexes_example_4] + List r4 = datastore.prepare(q4).asList(FetchOptions.Builder.withDefaults()); + out.printf("Got %d results from query 4.\n", r4.size()); + } +} diff --git a/appengine/datastore/indexes-perfect/src/main/webapp/WEB-INF/appengine-web.xml b/appengine/datastore/indexes-perfect/src/main/webapp/WEB-INF/appengine-web.xml new file mode 100644 index 00000000000..e9d8b21cb8f --- /dev/null +++ b/appengine/datastore/indexes-perfect/src/main/webapp/WEB-INF/appengine-web.xml @@ -0,0 +1,21 @@ + + + + YOUR-PROJECT-ID + YOUR-VERSION-ID + true + diff --git a/appengine/datastore/indexes-perfect/src/main/webapp/WEB-INF/datastore-indexes.xml b/appengine/datastore/indexes-perfect/src/main/webapp/WEB-INF/datastore-indexes.xml new file mode 100644 index 00000000000..444eced01ef --- /dev/null +++ b/appengine/datastore/indexes-perfect/src/main/webapp/WEB-INF/datastore-indexes.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + diff --git a/appengine/datastore/indexes-perfect/src/main/webapp/WEB-INF/web.xml b/appengine/datastore/indexes-perfect/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..049fd7a05e7 --- /dev/null +++ b/appengine/datastore/indexes-perfect/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,39 @@ + + + + + indexes-servlet + com.example.appengine.IndexesServlet + + + indexes-servlet + / + + + + + profile + /* + + + CONFIDENTIAL + + + diff --git a/appengine/datastore/indexes-perfect/src/test/java/com/example/appengine/IndexesServletTest.java b/appengine/datastore/indexes-perfect/src/test/java/com/example/appengine/IndexesServletTest.java new file mode 100644 index 00000000000..e82f5b302ab --- /dev/null +++ b/appengine/datastore/indexes-perfect/src/test/java/com/example/appengine/IndexesServletTest.java @@ -0,0 +1,83 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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 com.example.appengine; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.when; + +import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig; +import com.google.appengine.tools.development.testing.LocalServiceTestHelper; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.io.PrintWriter; +import java.io.StringWriter; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * Unit tests for {@link IndexesServlet}. + */ +@RunWith(JUnit4.class) +public class IndexesServletTest { + + private final LocalServiceTestHelper helper = + new LocalServiceTestHelper( + // Set no eventual consistency, that way queries return all results. + // https://cloud.google.com/appengine/docs/java/tools/localunittesting#Java_Writing_High_Replication_Datastore_tests + new LocalDatastoreServiceTestConfig() + .setDefaultHighRepJobPolicyUnappliedJobPercentage(0)); + + @Mock private HttpServletRequest mockRequest; + @Mock private HttpServletResponse mockResponse; + private StringWriter responseWriter; + private IndexesServlet servletUnderTest; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + helper.setUp(); + + // Set up a fake HTTP response. + responseWriter = new StringWriter(); + when(mockResponse.getWriter()).thenReturn(new PrintWriter(responseWriter)); + + servletUnderTest = new IndexesServlet(); + } + + @After + public void tearDown() { + helper.tearDown(); + } + + @Test + public void doGet_emptyDatastore_writesNoWidgets() throws Exception { + servletUnderTest.doGet(mockRequest, mockResponse); + + String response = responseWriter.toString(); + assertThat(response).contains("Got 0 results from query 1."); + assertThat(response).contains("Got 0 results from query 2."); + assertThat(response).contains("Got 0 results from query 3."); + assertThat(response).contains("Got 0 results from query 4."); + } +} diff --git a/appengine/datastore/indexes/pom.xml b/appengine/datastore/indexes/pom.xml new file mode 100644 index 00000000000..b6c95b9a3dd --- /dev/null +++ b/appengine/datastore/indexes/pom.xml @@ -0,0 +1,91 @@ + + + 4.0.0 + war + 1.0-SNAPSHOT + com.example.appengine + appengine-datastore-indexes + + com.google.cloud + doc-samples + 1.0.0 + ../../.. + + + + com.google.appengine + appengine-api-1.0-sdk + ${appengine.sdk.version} + + + javax.servlet + servlet-api + jar + provided + + + + + junit + junit + 4.10 + test + + + org.mockito + mockito-all + 1.10.19 + test + + + com.google.appengine + appengine-testing + ${appengine.sdk.version} + test + + + com.google.appengine + appengine-api-stubs + ${appengine.sdk.version} + test + + + com.google.appengine + appengine-tools-sdk + ${appengine.sdk.version} + test + + + com.google.truth + truth + 0.28 + test + + + + + ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + + + com.google.appengine + appengine-maven-plugin + ${appengine.sdk.version} + + + + diff --git a/appengine/datastore/indexes/src/main/java/com/example/appengine/IndexesServlet.java b/appengine/datastore/indexes/src/main/java/com/example/appengine/IndexesServlet.java new file mode 100644 index 00000000000..78605f64bf2 --- /dev/null +++ b/appengine/datastore/indexes/src/main/java/com/example/appengine/IndexesServlet.java @@ -0,0 +1,64 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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 com.example.appengine; + +import com.google.appengine.api.datastore.DatastoreService; +import com.google.appengine.api.datastore.DatastoreServiceFactory; +import com.google.appengine.api.datastore.Entity; +import com.google.appengine.api.datastore.FetchOptions; +import com.google.appengine.api.datastore.Query; +import com.google.appengine.api.datastore.Query.CompositeFilterOperator; +import com.google.appengine.api.datastore.Query.FilterOperator; +import com.google.appengine.api.datastore.Query.FilterPredicate; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.List; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * A servlet to demonstrate the use of Cloud Datastore indexes. + */ +public class IndexesServlet extends HttpServlet { + private final DatastoreService datastore; + + public IndexesServlet() { + datastore = DatastoreServiceFactory.getDatastoreService(); + } + + @Override + public void doGet(HttpServletRequest req, HttpServletResponse resp) + throws IOException, ServletException { + // [START exploding_index_example_1] + Query q = + new Query("Widget") + .setFilter( + CompositeFilterOperator.and( + new FilterPredicate("x", FilterOperator.EQUAL, 1), + new FilterPredicate("y", FilterOperator.EQUAL, 2))) + .addSort("date", Query.SortDirection.ASCENDING); + // [END exploding_index_example_1] + List results = datastore.prepare(q).asList(FetchOptions.Builder.withDefaults()); + + PrintWriter out = resp.getWriter(); + out.printf("Got %d widgets.\n", results.size()); + } +} diff --git a/appengine/datastore/indexes/src/main/webapp/WEB-INF/appengine-web.xml b/appengine/datastore/indexes/src/main/webapp/WEB-INF/appengine-web.xml new file mode 100644 index 00000000000..e9d8b21cb8f --- /dev/null +++ b/appengine/datastore/indexes/src/main/webapp/WEB-INF/appengine-web.xml @@ -0,0 +1,21 @@ + + + + YOUR-PROJECT-ID + YOUR-VERSION-ID + true + diff --git a/appengine/datastore/indexes/src/main/webapp/WEB-INF/datastore-indexes.xml b/appengine/datastore/indexes/src/main/webapp/WEB-INF/datastore-indexes.xml new file mode 100644 index 00000000000..106de35043d --- /dev/null +++ b/appengine/datastore/indexes/src/main/webapp/WEB-INF/datastore-indexes.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + diff --git a/appengine/datastore/indexes/src/main/webapp/WEB-INF/web.xml b/appengine/datastore/indexes/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..049fd7a05e7 --- /dev/null +++ b/appengine/datastore/indexes/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,39 @@ + + + + + indexes-servlet + com.example.appengine.IndexesServlet + + + indexes-servlet + / + + + + + profile + /* + + + CONFIDENTIAL + + + diff --git a/appengine/datastore/indexes/src/test/java/com/example/appengine/IndexesServletTest.java b/appengine/datastore/indexes/src/test/java/com/example/appengine/IndexesServletTest.java new file mode 100644 index 00000000000..daf6e8f3821 --- /dev/null +++ b/appengine/datastore/indexes/src/test/java/com/example/appengine/IndexesServletTest.java @@ -0,0 +1,77 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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 com.example.appengine; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.when; + +import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig; +import com.google.appengine.tools.development.testing.LocalServiceTestHelper; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.io.PrintWriter; +import java.io.StringWriter; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * Unit tests for {@link IndexesServlet}. + */ +@RunWith(JUnit4.class) +public class IndexesServletTest { + + private final LocalServiceTestHelper helper = + new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig()); + + @Mock private HttpServletRequest mockRequest; + @Mock private HttpServletResponse mockResponse; + private StringWriter responseWriter; + private IndexesServlet servletUnderTest; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + helper.setUp(); + + // Set up a fake HTTP response. + responseWriter = new StringWriter(); + when(mockResponse.getWriter()).thenReturn(new PrintWriter(responseWriter)); + + servletUnderTest = new IndexesServlet(); + } + + @After + public void tearDown() { + helper.tearDown(); + } + + @Test + public void doGet_emptyDatastore_writesNoWidgets() throws Exception { + servletUnderTest.doGet(mockRequest, mockResponse); + + assertThat(responseWriter.toString()) + .named("IndexesServlet response") + .isEqualTo("Got 0 widgets.\n"); + } +} diff --git a/appengine/datastore/src/test/java/com/example/appengine/IndexesTest.java b/appengine/datastore/src/test/java/com/example/appengine/IndexesTest.java new file mode 100644 index 00000000000..6a3e616bb5a --- /dev/null +++ b/appengine/datastore/src/test/java/com/example/appengine/IndexesTest.java @@ -0,0 +1,103 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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 com.example.appengine; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.appengine.api.datastore.DatastoreService; +import com.google.appengine.api.datastore.DatastoreServiceFactory; +import com.google.appengine.api.datastore.Entity; +import com.google.appengine.api.datastore.FetchOptions; +import com.google.appengine.api.datastore.Key; +import com.google.appengine.api.datastore.KeyFactory; +import com.google.appengine.api.datastore.Query; +import com.google.appengine.api.datastore.Query.Filter; +import com.google.appengine.api.datastore.Query.FilterOperator; +import com.google.appengine.api.datastore.Query.FilterPredicate; +import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig; +import com.google.appengine.tools.development.testing.LocalServiceTestHelper; +import com.google.common.collect.ImmutableList; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.List; + +/** + * Unit tests to demonstrate App Engine Datastore queries. + */ +@RunWith(JUnit4.class) +public class IndexesTest { + + private final LocalServiceTestHelper helper = + new LocalServiceTestHelper( + // Set no eventual consistency, that way queries return all results. + // https://cloud.google.com/appengine/docs/java/tools/localunittesting#Java_Writing_High_Replication_Datastore_tests + new LocalDatastoreServiceTestConfig() + .setDefaultHighRepJobPolicyUnappliedJobPercentage(0)); + + private DatastoreService datastore; + + @Before + public void setUp() { + helper.setUp(); + datastore = DatastoreServiceFactory.getDatastoreService(); + } + + @After + public void tearDown() { + helper.tearDown(); + } + + @Test + public void propertyFilterExample_returnsMatchingEntities() throws Exception { + // [START unindexed_properties_1] + DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); + + Key acmeKey = KeyFactory.createKey("Company", "Acme"); + + Entity tom = new Entity("Person", "Tom", acmeKey); + tom.setProperty("name", "Tom"); + tom.setProperty("age", 32); + datastore.put(tom); + + Entity lucy = new Entity("Person", "Lucy", acmeKey); + lucy.setProperty("name", "Lucy"); + lucy.setUnindexedProperty("age", 29); + datastore.put(lucy); + + Filter ageFilter = new FilterPredicate("age", FilterOperator.GREATER_THAN, 25); + + Query q = new Query("Person").setAncestor(acmeKey).setFilter(ageFilter); + + // Returns tom but not lucy, because her age is unindexed + List results = datastore.prepare(q).asList(FetchOptions.Builder.withDefaults()); + // [END unindexed_properties_1] + + assertThat(getKeys(results)).named("query result keys").containsExactly(tom.getKey()); + } + + private ImmutableList getKeys(List entities) { + ImmutableList.Builder keys = ImmutableList.builder(); + for (Entity entity : entities) { + keys.add(entity.getKey()); + } + return keys.build(); + } +} diff --git a/java-repo-tools/test-devserver.sh b/java-repo-tools/test-devserver.sh new file mode 100755 index 00000000000..7be4c6ab110 --- /dev/null +++ b/java-repo-tools/test-devserver.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# Copyright 2016 Google Inc. All Rights Reserved. +# +# 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. + +# Usage: +# test-devserver.sh path/to/project +# +# This script runs the local appengine:devserver Maven plugin and verifies that +# a request to http://localhost:8080/ does not return an error code. +# +# As an example, this is useful for verifying that datastore-indexes.xml is +# correct (only if autoGenerate=false and the / handler does all queries used), +# as an example. + +set -e +set -x + +if [ -z "$1" ]; then + echo "Missing directory parameter." + echo "Usage:" + echo " $0 path/to/project" + exit 1 +fi + +( +cd "$1" +expect -c ' + spawn mvn --batch-mode clean appengine:devserver -DskipTests + set timeout 600 + expect localhost:8080 + sleep 5 + spawn curl -f http://localhost:8080/ + lassign [wait -i $spawn_id] pid spawnid os_error_flag value + exit $value + ' +) + diff --git a/pom.xml b/pom.xml index e37c1554d2d..bd885080d87 100644 --- a/pom.xml +++ b/pom.xml @@ -47,6 +47,9 @@ appengine/appidentity appengine/channel appengine/datastore + appengine/datastore/indexes + appengine/datastore/indexes-exploding + appengine/datastore/indexes-perfect appengine/guestbook-objectify appengine/helloworld appengine/logs diff --git a/travis.sh b/travis.sh index fe0b680e9d9..7065ce69a80 100755 --- a/travis.sh +++ b/travis.sh @@ -24,3 +24,13 @@ if [ -z "$GOOGLE_APPLICATION_CREDENTIALS"]; then fi mvn --batch-mode clean verify -DskipTests=$SKIP_TESTS | egrep -v "(^\[INFO\] Download|^\[INFO\].*skipping)" +# Run tests using App Engine local devserver. +devserver_tests=( + appengine/datastore/indexes + appengine/datastore/indexes-exploding + appengine/datastore/indexes-perfect +) +for testdir in ${devserver_tests[@]} ; do + ./java-repo-tools/test-devserver.sh "${testdir}" +done +