From 76f1bd34ef0bfa97799bfec9ffabe82d04b520dd Mon Sep 17 00:00:00 2001 From: Jisha Abubaker <jishaa@google.com> Date: Mon, 25 Sep 2017 09:58:25 -0700 Subject: [PATCH 1/2] updating Firebase on App Engine sample to use latest libs fixing issue in running locally --- appengine/firebase-tictactoe/README.md | 44 +++--- appengine/firebase-tictactoe/pom.xml | 130 ++++++------------ .../appengine/firetactoe/DeleteServlet.java | 2 +- .../appengine/firetactoe/FirebaseChannel.java | 16 ++- .../example/appengine/firetactoe/Game.java | 20 +-- .../appengine/firetactoe/MoveServlet.java | 2 +- .../appengine/firetactoe/ObjectifyHelper.java | 1 + .../appengine/firetactoe/OpenedServlet.java | 2 +- .../firetactoe/TicTacToeServlet.java | 6 +- .../src/main/webapp/WEB-INF/view/index.jsp | 3 +- .../firetactoe/DeleteServletTest.java | 24 ++-- .../firetactoe/FirebaseChannelTest.java | 25 ++-- .../appengine/firetactoe/MoveServletTest.java | 23 ++-- .../firetactoe/OpenedServletTest.java | 20 +-- .../firetactoe/TicTacToeServletTest.java | 34 +++-- 15 files changed, 162 insertions(+), 190 deletions(-) diff --git a/appengine/firebase-tictactoe/README.md b/appengine/firebase-tictactoe/README.md index 2e94a9dc012..8a3889bf574 100644 --- a/appengine/firebase-tictactoe/README.md +++ b/appengine/firebase-tictactoe/README.md @@ -10,17 +10,11 @@ for realtime notifications when the board changes. ## Prerequisites * Install [Apache Maven][maven] 3.0.5 or later -* Install the [Google Cloud SDK][sdk] * Create a project in the [Firebase Console][fb-console] * In the [Overview section][fb-overview] of the Firebase console, click 'Add Firebase to your web app' and replace the contents of the file `src/main/webapp/WEB-INF/view/firebase_config.jspf` with that code snippet. -* If using the development appserver to run the app locally, you must supply - credentials that would otherwise be inferred from the App Engine environment. - Download [service account credentials][creds] and set the - `GOOGLE_APPLICATION_CREDENTIALS` environment variable to its path: - - export GOOGLE_APPLICATION_CREDENTIALS=/path/to/your/credentials.json +* [Enable the Identity API](https://console.cloud.google.com/apis/api/identitytoolkit.googleapis.com/overview) [fb-console]: https://console.firebase.google.com @@ -29,26 +23,36 @@ for realtime notifications when the board changes. [fb-overview]: https://console.firebase.google.com/project/_/overview [maven]: https://maven.apache.org +## Setup + +* If you haven't already, Download and initialize the [Cloud SDK](https://cloud.google.com/sdk/) +``` + gcloud init +``` +* If you haven't already, Create an App Engine app within the current Google Cloud Project +``` + gcloud app create +``` +* If you haven't already, setup +[Application Default Credentials](https://developers.google.com/identity/protocols/application-default-credentials) +to run your application locally: +Download [service account credentials][creds] and set the `GOOGLE_APPLICATION_CREDENTIALS` +environment variable to its path: +``` + export GOOGLE_APPLICATION_CREDENTIALS=/path/to/your/credentials.json +``` -## Run the sample -* To run the app locally using the development appserver: +### Running locally - ```sh $ mvn appengine:run - ``` -## Troubleshooting +When running locally, the page does not automatically refresh, +please reload the page manually after each move. -* If you see the error `Google Cloud SDK path was not provided ...`: - * Make sure you've installed the [Google Cloud SDK][sdk] - * Make sure the Google Cloud SDK's `bin/` directory is in your `PATH`. If - you prefer it not to be, you can also set the environment variable - `GOOGLE_CLOUD_SDK_HOME` to point to where you installed the SDK: +### Deploying to App Engine Standard - ```sh - export GOOGLE_CLOUD_SDK_HOME=/path/to/google-cloud-sdk - ``` + $ mvn appengine:deploy ## Contributing changes diff --git a/appengine/firebase-tictactoe/pom.xml b/appengine/firebase-tictactoe/pom.xml index 9949658ce49..af21b34fde6 100644 --- a/appengine/firebase-tictactoe/pom.xml +++ b/appengine/firebase-tictactoe/pom.xml @@ -25,95 +25,55 @@ <version>1.0.0</version> <relativePath>..</relativePath> </parent> - <properties> - <objectify.version>5.1.21</objectify.version> - <servlet-api.version>2.5</servlet-api.version> - <gson.version>2.7</gson.version> - <guava.version>20.0</guava.version> - <google-api-client.version>1.22.0</google-api-client.version> - <junit.version>4.12</junit.version> - <mockito.version>1.10.19</mockito.version> - <google-truth.version>0.34</google-truth.version> - <appengine-maven.version>1.3.1</appengine-maven.version> - </properties> <!-- [START set_versions] --> <prerequisites> <maven>3.3.9</maven> </prerequisites> <!-- [END set_versions] --> - <dependencies> - <dependency> - <groupId>com.google.appengine</groupId> - <artifactId>appengine-api-1.0-sdk</artifactId> - <version>${appengine.sdk.version}</version> - </dependency> - <dependency> - <groupId>javax.servlet</groupId> - <artifactId>servlet-api</artifactId> - <version>${servlet-api.version}</version> - <type>jar</type> - <scope>provided</scope> - </dependency> - <dependency> - <groupId>com.google.code.gson</groupId> - <artifactId>gson</artifactId> - <version>2.8.1</version> - </dependency> - <dependency> - <groupId>com.googlecode.objectify</groupId> - <artifactId>objectify</artifactId> - <version>${objectify.version}</version> - </dependency> - <dependency> - <groupId>com.google.guava</groupId> - <artifactId>guava</artifactId> - <version>${guava.version}</version> - </dependency> - <dependency> - <groupId>com.google.api-client</groupId> - <artifactId>google-api-client-appengine</artifactId> - <version>${google-api-client.version}</version> - </dependency> - - - <!-- Test Dependencies --> - <dependency> - <groupId>junit</groupId> - <artifactId>junit</artifactId> - <version>${junit.version}</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-all</artifactId> - <version>${mockito.version}</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>com.google.appengine</groupId> - <artifactId>appengine-testing</artifactId> - <version>${appengine.sdk.version}</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>com.google.appengine</groupId> - <artifactId>appengine-api-stubs</artifactId> - <version>${appengine.sdk.version}</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>com.google.appengine</groupId> - <artifactId>appengine-tools-sdk</artifactId> - <version>${appengine.sdk.version}</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>com.google.truth</groupId> - <artifactId>truth</artifactId> - <version>${google-truth.version}</version> - <scope>test</scope> - </dependency> - </dependencies> + <dependencies> + <dependency> + <groupId>com.googlecode.objectify</groupId> + <artifactId>objectify</artifactId> + <version>5.1.21</version> + </dependency> + <dependency> + <groupId>com.google.appengine</groupId> + <artifactId>appengine-api-1.0-sdk</artifactId> + <version>1.9.56</version> + </dependency> + <dependency> + <groupId>com.google.api-client</groupId> + <artifactId>google-api-client-appengine</artifactId> + <version>1.22.0</version> + </dependency> + <dependency> + <groupId>com.google.code.gson</groupId> + <artifactId>gson</artifactId> + <version>2.8.1</version> + </dependency> + <!-- Test dependences --> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.12</version> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <version>2.9.0</version> + </dependency> + <dependency> + <groupId>com.google.appengine</groupId> + <artifactId>appengine-api-stubs</artifactId> + <version>1.9.56</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.google.appengine</groupId> + <artifactId>appengine-testing</artifactId> + <version>1.9.56</version> + </dependency> + </dependencies> <build> <!-- for hot reload of the web application --> <outputDirectory>${project.build.directory}/${project.build.finalName}/WEB-INF/classes @@ -122,7 +82,7 @@ <plugin> <groupId>com.google.cloud.tools</groupId> <artifactId>appengine-maven-plugin</artifactId> - <version>${appengine-maven.version}</version> + <version>1.3.1</version> </plugin> </plugins> </build> diff --git a/appengine/firebase-tictactoe/src/main/java/com/example/appengine/firetactoe/DeleteServlet.java b/appengine/firebase-tictactoe/src/main/java/com/example/appengine/firetactoe/DeleteServlet.java index 1988424fbb6..f58514fe3d1 100644 --- a/appengine/firebase-tictactoe/src/main/java/com/example/appengine/firetactoe/DeleteServlet.java +++ b/appengine/firebase-tictactoe/src/main/java/com/example/appengine/firetactoe/DeleteServlet.java @@ -42,6 +42,6 @@ public void doPost(HttpServletRequest request, HttpServletResponse response) String currentUserId = userService.getCurrentUser().getUserId(); // TODO(you): In practice, first validate that the user has permission to delete the Game - game.deleteChannel(currentUserId); + game.deleteChannel(getServletContext(), currentUserId); } } diff --git a/appengine/firebase-tictactoe/src/main/java/com/example/appengine/firetactoe/FirebaseChannel.java b/appengine/firebase-tictactoe/src/main/java/com/example/appengine/firetactoe/FirebaseChannel.java index 5c859d65dbe..f7193543219 100644 --- a/appengine/firebase-tictactoe/src/main/java/com/example/appengine/firetactoe/FirebaseChannel.java +++ b/appengine/firebase-tictactoe/src/main/java/com/example/appengine/firetactoe/FirebaseChannel.java @@ -24,13 +24,13 @@ import com.google.api.client.http.HttpRequestFactory; import com.google.api.client.http.HttpResponse; import com.google.api.client.http.HttpTransport; +import com.google.api.client.util.Preconditions; import com.google.appengine.api.appidentity.AppIdentityService; import com.google.appengine.api.appidentity.AppIdentityServiceFactory; import com.google.common.io.BaseEncoding; import com.google.common.io.CharStreams; import com.google.gson.Gson; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -39,6 +39,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.Map; +import javax.servlet.ServletContext; /** * Utility functions for communicating with the realtime communication channel using Firebase. @@ -47,7 +48,7 @@ * Firebase. */ public class FirebaseChannel { - private static final String FIREBASE_SNIPPET_PATH = "WEB-INF/view/firebase_config.jspf"; + private static final String FIREBASE_SNIPPET_PATH = "/WEB-INF/view/firebase_config.jspf"; static InputStream firebaseConfigStream = null; private static final Collection FIREBASE_SCOPES = Arrays.asList( "https://www.googleapis.com/auth/firebase.database", @@ -67,9 +68,9 @@ public class FirebaseChannel { * FirebaseChannel is a singleton, since it's just utility functions. * The class derives auth information when first instantiated. */ - public static FirebaseChannel getInstance() { + public static FirebaseChannel getInstance(ServletContext servletContext) { if (instance == null) { - instance = new FirebaseChannel(); + instance = new FirebaseChannel(servletContext); } return instance; } @@ -81,11 +82,13 @@ public static FirebaseChannel getInstance() { * communicate with Firebase is derived from App Engine's default credentials, and given * Firebase's OAuth scopes. */ - private FirebaseChannel() { + private FirebaseChannel(ServletContext servletContext) { try { // This variables exist primarily so it can be stubbed out in unit tests. if (null == firebaseConfigStream) { - firebaseConfigStream = new FileInputStream(FIREBASE_SNIPPET_PATH); + Preconditions.checkNotNull(servletContext, + "Servlet context expected to initialize Firebase channel"); + firebaseConfigStream = servletContext.getResourceAsStream(FIREBASE_SNIPPET_PATH); } String firebaseSnippet = CharStreams.toString(new InputStreamReader( @@ -176,7 +179,6 @@ public String createFirebaseToken(Game game, String userId) { // The following methods are to illustrate making various calls to Firebase from App Engine // Standard - public HttpResponse firebasePut(String path, Object object) throws IOException { // Make requests auth'ed using Application Default Credentials Credential credential = GoogleCredential.getApplicationDefault().createScoped(FIREBASE_SCOPES); diff --git a/appengine/firebase-tictactoe/src/main/java/com/example/appengine/firetactoe/Game.java b/appengine/firebase-tictactoe/src/main/java/com/example/appengine/firetactoe/Game.java index 7f6a1e84135..6acb7e486ea 100644 --- a/appengine/firebase-tictactoe/src/main/java/com/example/appengine/firetactoe/Game.java +++ b/appengine/firebase-tictactoe/src/main/java/com/example/appengine/firetactoe/Game.java @@ -24,11 +24,11 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Pattern; +import javax.servlet.ServletContext; /** * The datastore-persisted Game object. This holds the entire game state - from a representation of * the board, to the players are and whose turn it is, and who the winner is and how they won. - * * It also contains some convenience functions for communicating updates to the board to the * clients, via Firebase. */ @@ -109,26 +109,26 @@ public String getChannelKey(String userId) { return userId + id; } - public void deleteChannel(String userId) + public void deleteChannel(ServletContext servletContext, String userId) throws IOException { if (userId != null) { String channelKey = getChannelKey(userId); - FirebaseChannel.getInstance().sendFirebaseMessage(channelKey, null); + FirebaseChannel.getInstance(servletContext).sendFirebaseMessage(channelKey, null); } } - private void sendUpdateToUser(String userId) + private void sendUpdateToUser(ServletContext servletContext, String userId) throws IOException { if (userId != null) { String channelKey = getChannelKey(userId); - FirebaseChannel.getInstance().sendFirebaseMessage(channelKey, this); + FirebaseChannel.getInstance(servletContext).sendFirebaseMessage(channelKey, this); } } - public void sendUpdateToClients() + public void sendUpdateToClients(ServletContext servletContext) throws IOException { - sendUpdateToUser(userX); - sendUpdateToUser(userO); + sendUpdateToUser(servletContext, userX); + sendUpdateToUser(servletContext, userO); } // [END send_updates] @@ -152,7 +152,7 @@ public void checkWin() { } } - public boolean makeMove(int position, String userId) { + public boolean makeMove(ServletContext servletContext, int position, String userId) { String currentMovePlayer; char value; if (getMoveX()) { @@ -170,7 +170,7 @@ public boolean makeMove(int position, String userId) { checkWin(); setMoveX(!getMoveX()); try { - sendUpdateToClients(); + sendUpdateToClients(servletContext); } catch (IOException e) { LOGGER.log(Level.SEVERE, "Error sending Game update to Firebase", e); throw new RuntimeException(e); diff --git a/appengine/firebase-tictactoe/src/main/java/com/example/appengine/firetactoe/MoveServlet.java b/appengine/firebase-tictactoe/src/main/java/com/example/appengine/firetactoe/MoveServlet.java index 4911d22bc37..354e346c6f2 100644 --- a/appengine/firebase-tictactoe/src/main/java/com/example/appengine/firetactoe/MoveServlet.java +++ b/appengine/firebase-tictactoe/src/main/java/com/example/appengine/firetactoe/MoveServlet.java @@ -43,7 +43,7 @@ public void doPost(HttpServletRequest request, HttpServletResponse response) String currentUserId = userService.getCurrentUser().getUserId(); int cell = new Integer(request.getParameter("cell")); - if (!game.makeMove(cell, currentUserId)) { + if (!game.makeMove(getServletContext(), cell, currentUserId)) { response.sendError(HttpServletResponse.SC_FORBIDDEN); } else { ofy.save().entity(game).now(); diff --git a/appengine/firebase-tictactoe/src/main/java/com/example/appengine/firetactoe/ObjectifyHelper.java b/appengine/firebase-tictactoe/src/main/java/com/example/appengine/firetactoe/ObjectifyHelper.java index abc1a28e33c..0544e7b87fa 100644 --- a/appengine/firebase-tictactoe/src/main/java/com/example/appengine/firetactoe/ObjectifyHelper.java +++ b/appengine/firebase-tictactoe/src/main/java/com/example/appengine/firetactoe/ObjectifyHelper.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.example.appengine.firetactoe; import com.googlecode.objectify.ObjectifyService; diff --git a/appengine/firebase-tictactoe/src/main/java/com/example/appengine/firetactoe/OpenedServlet.java b/appengine/firebase-tictactoe/src/main/java/com/example/appengine/firetactoe/OpenedServlet.java index 2e71f9391cb..5efcc21e6a2 100644 --- a/appengine/firebase-tictactoe/src/main/java/com/example/appengine/firetactoe/OpenedServlet.java +++ b/appengine/firebase-tictactoe/src/main/java/com/example/appengine/firetactoe/OpenedServlet.java @@ -35,6 +35,6 @@ public void doPost(HttpServletRequest request, HttpServletResponse response) String gameId = request.getParameter("gameKey"); Objectify ofy = ObjectifyService.ofy(); Game game = ofy.load().type(Game.class).id(gameId).safe(); - game.sendUpdateToClients(); + game.sendUpdateToClients(getServletContext()); } } diff --git a/appengine/firebase-tictactoe/src/main/java/com/example/appengine/firetactoe/TicTacToeServlet.java b/appengine/firebase-tictactoe/src/main/java/com/example/appengine/firetactoe/TicTacToeServlet.java index 8b248577971..d34d63fed7f 100644 --- a/appengine/firebase-tictactoe/src/main/java/com/example/appengine/firetactoe/TicTacToeServlet.java +++ b/appengine/firebase-tictactoe/src/main/java/com/example/appengine/firetactoe/TicTacToeServlet.java @@ -24,6 +24,7 @@ import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; +import java.net.URL; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; @@ -84,14 +85,15 @@ public void doGet(HttpServletRequest request, HttpServletResponse response) } // 2. Create this Game in the firebase db - game.sendUpdateToClients(); + game.sendUpdateToClients(getServletContext()); // 3. Inject a secure token into the client, so it can get game updates // [START pass_token] // The 'Game' object exposes a method which creates a unique string based on the game's key // and the user's id. - String token = FirebaseChannel.getInstance().createFirebaseToken(game, userId); + String token = FirebaseChannel.getInstance(getServletContext()) + .createFirebaseToken(game, userId); request.setAttribute("token", token); // 4. More general template values diff --git a/appengine/firebase-tictactoe/src/main/webapp/WEB-INF/view/index.jsp b/appengine/firebase-tictactoe/src/main/webapp/WEB-INF/view/index.jsp index fab4ac6f0ec..f037d891fb0 100644 --- a/appengine/firebase-tictactoe/src/main/webapp/WEB-INF/view/index.jsp +++ b/appengine/firebase-tictactoe/src/main/webapp/WEB-INF/view/index.jsp @@ -32,8 +32,7 @@ <div id="display-area" class="waiting"> <h2>Firebase-enabled Tic Tac Toe</h2> <div id="other-player"> - Waiting for another player to join.<br> - Send them this link to play:<br> + Send the other player this link to play, and click link below to wait on the other player:<br> <div id="game-link"><a href="<%= request.getAttribute("game_link") %>"><%= request.getAttribute("game_link") %></a></div> </div> <div id="your-move">Your move! Click a square to place your piece.</div> diff --git a/appengine/firebase-tictactoe/src/test/java/com/example/appengine/firetactoe/DeleteServletTest.java b/appengine/firebase-tictactoe/src/test/java/com/example/appengine/firetactoe/DeleteServletTest.java index 0df68aaaefc..c466695c841 100644 --- a/appengine/firebase-tictactoe/src/test/java/com/example/appengine/firetactoe/DeleteServletTest.java +++ b/appengine/firebase-tictactoe/src/test/java/com/example/appengine/firetactoe/DeleteServletTest.java @@ -16,7 +16,7 @@ package com.example.appengine.firetactoe; -import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.spy; @@ -38,6 +38,11 @@ import com.googlecode.objectify.ObjectifyFactory; import com.googlecode.objectify.ObjectifyService; import com.googlecode.objectify.util.Closeable; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.HashMap; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; @@ -46,14 +51,9 @@ import org.junit.runners.JUnit4; import org.mockito.Matchers; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.util.HashMap; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - /** * Unit tests for {@link DeleteServlet}. */ @@ -98,11 +98,11 @@ public void setUp() throws Exception { helper.setUp(); dbSession = ObjectifyService.begin(); - servletUnderTest = new DeleteServlet(); + servletUnderTest = spy(new DeleteServlet()); helper.setEnvIsLoggedIn(true); // Make sure there are no firebase requests if we don't expect it - FirebaseChannel.getInstance().httpTransport = null; + FirebaseChannel.getInstance(null).httpTransport = null; } @After @@ -117,7 +117,7 @@ public void doPost_noGameKey() throws Exception { servletUnderTest.doPost(mockRequest, mockResponse); fail("Should not succeed with no gameKey specified."); } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).startsWith("id 'null'"); + assertTrue(e.getMessage().startsWith("id 'null'")); } } @@ -145,8 +145,8 @@ public LowLevelHttpResponse execute() throws IOException { }; } }); - FirebaseChannel.getInstance().httpTransport = mockHttpTransport; - + FirebaseChannel.getInstance(null).httpTransport = mockHttpTransport; + Mockito.doReturn(null).when(servletUnderTest).getServletContext(); servletUnderTest.doPost(mockRequest, mockResponse); verify(mockHttpTransport, times(1)).buildRequest( diff --git a/appengine/firebase-tictactoe/src/test/java/com/example/appengine/firetactoe/FirebaseChannelTest.java b/appengine/firebase-tictactoe/src/test/java/com/example/appengine/firetactoe/FirebaseChannelTest.java index 0c92863f77e..5f54243240c 100644 --- a/appengine/firebase-tictactoe/src/test/java/com/example/appengine/firetactoe/FirebaseChannelTest.java +++ b/appengine/firebase-tictactoe/src/test/java/com/example/appengine/firetactoe/FirebaseChannelTest.java @@ -16,7 +16,7 @@ package com.example.appengine.firetactoe; -import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -28,6 +28,8 @@ import com.google.api.client.testing.http.MockLowLevelHttpResponse; import com.google.appengine.tools.development.testing.LocalAppIdentityServiceTestConfig; import com.google.appengine.tools.development.testing.LocalServiceTestHelper; +import java.io.ByteArrayInputStream; +import java.io.IOException; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; @@ -36,9 +38,6 @@ import org.junit.runners.JUnit4; import org.mockito.MockitoAnnotations; -import java.io.ByteArrayInputStream; -import java.io.IOException; - /** * Unit tests for {@link FirebaseChannel}. */ @@ -56,7 +55,7 @@ public static void setUpBeforeClass() { FirebaseChannel.firebaseConfigStream = new ByteArrayInputStream( String.format("databaseURL: \"%s\"", FIREBASE_DB_URL).getBytes()); - firebaseChannel = FirebaseChannel.getInstance(); + firebaseChannel = FirebaseChannel.getInstance(null); } @Before @@ -87,7 +86,7 @@ public LowLevelHttpResponse execute() throws IOException { }; } }); - FirebaseChannel.getInstance().httpTransport = mockHttpTransport; + FirebaseChannel.getInstance(null).httpTransport = mockHttpTransport; firebaseChannel.sendFirebaseMessage("my_key", new Game()); @@ -112,7 +111,7 @@ public LowLevelHttpResponse execute() throws IOException { }; } }); - FirebaseChannel.getInstance().httpTransport = mockHttpTransport; + FirebaseChannel.getInstance(null).httpTransport = mockHttpTransport; firebaseChannel.sendFirebaseMessage("my_key", null); @@ -126,7 +125,7 @@ public void createFirebaseToken() throws Exception { String jwt = firebaseChannel.createFirebaseToken(game, "userId"); - assertThat(jwt).matches("^([\\w+/=-]+\\.){2}[\\w+/=-]+$"); + assertTrue(jwt.matches("^([\\w+/=-]+\\.){2}[\\w+/=-]+$")); } @Test @@ -146,7 +145,7 @@ public LowLevelHttpResponse execute() throws IOException { }; } }); - FirebaseChannel.getInstance().httpTransport = mockHttpTransport; + FirebaseChannel.getInstance(null).httpTransport = mockHttpTransport; Game game = new Game(); firebaseChannel.firebasePut(FIREBASE_DB_URL + "/my/path", game); @@ -171,7 +170,7 @@ public LowLevelHttpResponse execute() throws IOException { }; } }); - FirebaseChannel.getInstance().httpTransport = mockHttpTransport; + FirebaseChannel.getInstance(null).httpTransport = mockHttpTransport; Game game = new Game(); firebaseChannel.firebasePatch(FIREBASE_DB_URL + "/my/path", game); @@ -196,7 +195,7 @@ public LowLevelHttpResponse execute() throws IOException { }; } }); - FirebaseChannel.getInstance().httpTransport = mockHttpTransport; + FirebaseChannel.getInstance(null).httpTransport = mockHttpTransport; Game game = new Game(); firebaseChannel.firebasePost(FIREBASE_DB_URL + "/my/path", game); @@ -221,7 +220,7 @@ public LowLevelHttpResponse execute() throws IOException { }; } }); - FirebaseChannel.getInstance().httpTransport = mockHttpTransport; + FirebaseChannel.getInstance(null).httpTransport = mockHttpTransport; firebaseChannel.firebaseGet(FIREBASE_DB_URL + "/my/path"); @@ -245,7 +244,7 @@ public LowLevelHttpResponse execute() throws IOException { }; } }); - FirebaseChannel.getInstance().httpTransport = mockHttpTransport; + FirebaseChannel.getInstance(null).httpTransport = mockHttpTransport; firebaseChannel.firebaseDelete(FIREBASE_DB_URL + "/my/path"); diff --git a/appengine/firebase-tictactoe/src/test/java/com/example/appengine/firetactoe/MoveServletTest.java b/appengine/firebase-tictactoe/src/test/java/com/example/appengine/firetactoe/MoveServletTest.java index ac07a251028..814d7a6fc8e 100644 --- a/appengine/firebase-tictactoe/src/test/java/com/example/appengine/firetactoe/MoveServletTest.java +++ b/appengine/firebase-tictactoe/src/test/java/com/example/appengine/firetactoe/MoveServletTest.java @@ -16,7 +16,7 @@ package com.example.appengine.firetactoe; -import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; @@ -37,6 +37,11 @@ import com.googlecode.objectify.ObjectifyFactory; import com.googlecode.objectify.ObjectifyService; import com.googlecode.objectify.util.Closeable; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.HashMap; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; @@ -45,14 +50,9 @@ import org.junit.runners.JUnit4; import org.mockito.Matchers; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.util.HashMap; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - /** * Unit tests for {@link MoveServlet}. */ @@ -97,11 +97,11 @@ public void setUp() throws Exception { helper.setUp(); dbSession = ObjectifyService.begin(); - servletUnderTest = new MoveServlet(); + servletUnderTest = spy(new MoveServlet()); helper.setEnvIsLoggedIn(true); // Make sure there are no firebase requests if we don't expect it - FirebaseChannel.getInstance().httpTransport = null; + FirebaseChannel.getInstance(null).httpTransport = null; } @After @@ -136,12 +136,13 @@ public LowLevelHttpResponse execute() throws IOException { }; } }); - FirebaseChannel.getInstance().httpTransport = mockHttpTransport; + FirebaseChannel.getInstance(null).httpTransport = mockHttpTransport; + Mockito.doReturn(null).when(servletUnderTest).getServletContext(); servletUnderTest.doPost(mockRequest, mockResponse); game = ofy.load().type(Game.class).id(gameKey).safe(); - assertThat(game.board).isEqualTo(" X "); + assertEquals(game.board, " X "); verify(mockHttpTransport, times(2)).buildRequest( eq("PATCH"), Matchers.matches(FIREBASE_DB_URL + "/channels/[\\w-]+.json$")); diff --git a/appengine/firebase-tictactoe/src/test/java/com/example/appengine/firetactoe/OpenedServletTest.java b/appengine/firebase-tictactoe/src/test/java/com/example/appengine/firetactoe/OpenedServletTest.java index 9b2990196f6..d3a97eded0c 100644 --- a/appengine/firebase-tictactoe/src/test/java/com/example/appengine/firetactoe/OpenedServletTest.java +++ b/appengine/firebase-tictactoe/src/test/java/com/example/appengine/firetactoe/OpenedServletTest.java @@ -36,6 +36,11 @@ import com.googlecode.objectify.ObjectifyFactory; import com.googlecode.objectify.ObjectifyService; import com.googlecode.objectify.util.Closeable; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.HashMap; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; @@ -44,14 +49,9 @@ import org.junit.runners.JUnit4; import org.mockito.Matchers; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.util.HashMap; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - /** * Unit tests for {@link OpenedServlet}. */ @@ -96,11 +96,11 @@ public void setUp() throws Exception { helper.setUp(); dbSession = ObjectifyService.begin(); - servletUnderTest = new OpenedServlet(); + servletUnderTest = spy(new OpenedServlet()); helper.setEnvIsLoggedIn(true); // Make sure there are no firebase requests if we don't expect it - FirebaseChannel.getInstance().httpTransport = null; + FirebaseChannel.getInstance(null).httpTransport = null; } @After @@ -134,8 +134,8 @@ public LowLevelHttpResponse execute() throws IOException { }; } }); - FirebaseChannel.getInstance().httpTransport = mockHttpTransport; - + FirebaseChannel.getInstance(null).httpTransport = mockHttpTransport; + Mockito.doReturn(null).when(servletUnderTest).getServletContext(); servletUnderTest.doPost(mockRequest, mockResponse); verify(mockHttpTransport, times(2)).buildRequest( diff --git a/appengine/firebase-tictactoe/src/test/java/com/example/appengine/firetactoe/TicTacToeServletTest.java b/appengine/firebase-tictactoe/src/test/java/com/example/appengine/firetactoe/TicTacToeServletTest.java index 7f5fdf90150..de180b2d8e8 100644 --- a/appengine/firebase-tictactoe/src/test/java/com/example/appengine/firetactoe/TicTacToeServletTest.java +++ b/appengine/firebase-tictactoe/src/test/java/com/example/appengine/firetactoe/TicTacToeServletTest.java @@ -16,7 +16,7 @@ package com.example.appengine.firetactoe; -import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.spy; @@ -38,6 +38,13 @@ import com.googlecode.objectify.ObjectifyFactory; import com.googlecode.objectify.ObjectifyService; import com.googlecode.objectify.util.Closeable; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.lang.StringBuffer; +import java.util.HashMap; +import javax.servlet.RequestDispatcher; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; @@ -46,16 +53,9 @@ import org.junit.runners.JUnit4; import org.mockito.Matchers; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.lang.StringBuffer; -import java.util.HashMap; -import javax.servlet.RequestDispatcher; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - /** * Unit tests for {@link TicTacToeServlet}. */ @@ -105,7 +105,7 @@ public void setUp() throws Exception { when(mockRequest.getRequestURL()).thenReturn(new StringBuffer("https://timbre/")); when(mockRequest.getRequestDispatcher("/WEB-INF/view/index.jsp")).thenReturn(requestDispatcher); - servletUnderTest = new TicTacToeServlet(); + servletUnderTest = spy(new TicTacToeServlet()); helper.setEnvIsLoggedIn(true); } @@ -133,14 +133,16 @@ public LowLevelHttpResponse execute() throws IOException { }; } }); - FirebaseChannel.getInstance().httpTransport = mockHttpTransport; + FirebaseChannel.getInstance(null).httpTransport = mockHttpTransport; + + Mockito.doReturn(null).when(servletUnderTest).getServletContext(); servletUnderTest.doGet(mockRequest, mockResponse); // Make sure the game object was created for a new game Objectify ofy = ObjectifyService.ofy(); Game game = ofy.load().type(Game.class).first().safe(); - assertThat(game.userX).isEqualTo(USER_ID); + assertEquals(game.userX, USER_ID); verify(mockHttpTransport, times(1)).buildRequest( eq("PATCH"), Matchers.matches(FIREBASE_DB_URL + "/channels/[\\w-]+.json$")); @@ -170,7 +172,7 @@ public LowLevelHttpResponse execute() throws IOException { }; } }); - FirebaseChannel.getInstance().httpTransport = mockHttpTransport; + FirebaseChannel.getInstance(null).httpTransport = mockHttpTransport; // Insert a game Objectify ofy = ObjectifyService.ofy(); @@ -180,12 +182,14 @@ public LowLevelHttpResponse execute() throws IOException { when(mockRequest.getParameter("gameKey")).thenReturn(gameKey); + Mockito.doReturn(null).when(servletUnderTest).getServletContext(); + servletUnderTest.doGet(mockRequest, mockResponse); // Make sure the game object was updated with the other player game = ofy.load().type(Game.class).first().safe(); - assertThat(game.userX).isEqualTo("some-other-user-id"); - assertThat(game.userO).isEqualTo(USER_ID); + assertEquals(game.userX, "some-other-user-id"); + assertEquals(game.userO, USER_ID); verify(mockHttpTransport, times(2)).buildRequest( eq("PATCH"), Matchers.matches(FIREBASE_DB_URL + "/channels/[\\w-]+.json$")); From 633dbf7eab3b47495fc6c285f4e33633cf743293 Mon Sep 17 00:00:00 2001 From: Jisha Abubaker <jishaa@google.com> Date: Mon, 25 Sep 2017 13:27:33 -0700 Subject: [PATCH 2/2] readme updates --- appengine/firebase-tictactoe/README.md | 34 ++++++++++++-------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/appengine/firebase-tictactoe/README.md b/appengine/firebase-tictactoe/README.md index 8a3889bf574..4e2799844e7 100644 --- a/appengine/firebase-tictactoe/README.md +++ b/appengine/firebase-tictactoe/README.md @@ -7,34 +7,25 @@ for realtime notifications when the board changes. [Firebase]: https://firebase.google.com [standard]: https://cloud.google.com/appengine/docs/about-the-standard-environment -## Prerequisites +## Setup * Install [Apache Maven][maven] 3.0.5 or later * Create a project in the [Firebase Console][fb-console] +* Download and install the [Cloud SDK](https://cloud.google.com/sdk/) + +Initialize the `gcloud` configuration to use your new project: +``` + gcloud init +``` * In the [Overview section][fb-overview] of the Firebase console, click 'Add Firebase to your web app' and replace the contents of the file `src/main/webapp/WEB-INF/view/firebase_config.jspf` with that code snippet. * [Enable the Identity API](https://console.cloud.google.com/apis/api/identitytoolkit.googleapis.com/overview) - - -[fb-console]: https://console.firebase.google.com -[sdk]: https://cloud.google.com/sdk -[creds]: https://console.firebase.google.com/iam-admin/serviceaccounts/project?project=_&consoleReturnUrl=https:%2F%2Fconsole.firebase.google.com%2Fproject%2F_%2Fsettings%2Fgeneral%2F -[fb-overview]: https://console.firebase.google.com/project/_/overview -[maven]: https://maven.apache.org - -## Setup - -* If you haven't already, Download and initialize the [Cloud SDK](https://cloud.google.com/sdk/) -``` - gcloud init -``` * If you haven't already, Create an App Engine app within the current Google Cloud Project ``` gcloud app create ``` -* If you haven't already, setup -[Application Default Credentials](https://developers.google.com/identity/protocols/application-default-credentials) +* Setup [Application Default Credentials](https://developers.google.com/identity/protocols/application-default-credentials) to run your application locally: Download [service account credentials][creds] and set the `GOOGLE_APPLICATION_CREDENTIALS` environment variable to its path: @@ -42,15 +33,20 @@ environment variable to its path: export GOOGLE_APPLICATION_CREDENTIALS=/path/to/your/credentials.json ``` +[fb-console]: https://console.firebase.google.com +[sdk]: https://cloud.google.com/sdk +[creds]: https://console.firebase.google.com/iam-admin/serviceaccounts/project?project=_&consoleReturnUrl=https:%2F%2Fconsole.firebase.google.com%2Fproject%2F_%2Fsettings%2Fgeneral%2F +[fb-overview]: https://console.firebase.google.com/project/_/overview +[maven]: https://maven.apache.org -### Running locally +## Running locally $ mvn appengine:run When running locally, the page does not automatically refresh, please reload the page manually after each move. -### Deploying to App Engine Standard +## Deploying to App Engine Standard $ mvn appengine:deploy