-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Add GAE standard + firebase tictactoe sample #358
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Current coverage is 48.19% (diff: 100%)@@ master #358 diff @@
==========================================
Files 73 73
Lines 2268 2268
Methods 0 0
Messages 0 0
Branches 158 158
==========================================
Hits 1093 1093
Misses 1145 1145
Partials 30 30
|
lesv
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good start.
- Why use both Objectify and Firebase?
- Should use new maven plugin.
- Could use a few comments everywhere - esp. where api's are used.
- Either do something on exceptions or don't catch them.
- Why use old nomenclature? channelId?
| <relativePath>../..</relativePath> | ||
| </parent> | ||
| <properties> | ||
| <objectify.version>5.1.13</objectify.version> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. I never really got the point of specifying the version numbers as a separate property, though, so if you don't know either, I can just specify it as a literal in the dependency section ;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No the repo-gardener requires it to be in properties, so thats a good thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, for whatever reason the versions plugin can detect version upgrades for plugins, but it can only update properties and dependency versions, not plugin versions directly.
appengine/firebase-tictactoe/pom.xml
Outdated
| <scope>provided</scope> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.json</groupId> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah - communicating with firebase is all through json
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I typically use this as it's very easy to use, but consider Google GSON. I'm trying to switch.
| <version>20160810</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>com.googlecode.objectify</groupId> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup - in this app, datastore is the canonical representation of the game board, and we use objectify to persist it.
appengine/firebase-tictactoe/pom.xml
Outdated
| <plugins> | ||
| <!-- Parent POM defines ${appengine.sdk.version} (updates frequently). --> | ||
| <plugin> | ||
| <groupId>com.google.appengine</groupId> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Old plugin, please use the new one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ugh... oddly enough, switching to the new plugin causes firebase calls to fail..
Updating for now. Working on getting it to work with the new plugin..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@GoogleCloudPlatform/cloud-tools-for-java FYI
| public void doPost(HttpServletRequest req, HttpServletResponse resp) | ||
| throws IOException { | ||
| UserService userService = UserServiceFactory.getUserService(); | ||
| String gameId = req.getParameter("g"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"g" -- shouldn't it be a bit longer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dunno - this is what the python query parameter was. The channel sample alternated between calling it "g" and "gameKey".
shrug Updated to be more descriptive.
| var config = { | ||
| apiKey: "AIzaSyC3gJDK1r-8nZuJGdmXntJR4E8cqH8qSTk", | ||
| authDomain: "channels-api-deprecation.firebaseapp.com", | ||
| databaseURL: "https://channels-api-deprecation.firebaseio.com", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
| apiKey: "AIzaSyC3gJDK1r-8nZuJGdmXntJR4E8cqH8qSTk", | ||
| authDomain: "channels-api-deprecation.firebaseapp.com", | ||
| databaseURL: "https://channels-api-deprecation.firebaseio.com", | ||
| storageBucket: "channels-api-deprecation.appspot.com", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
| authDomain: "channels-api-deprecation.firebaseapp.com", | ||
| databaseURL: "https://channels-api-deprecation.firebaseio.com", | ||
| storageBucket: "channels-api-deprecation.appspot.com", | ||
| messagingSenderId: "542117602304" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
| limitations under the License. | ||
| --> | ||
| <appengine-web-app xmlns="http://appengine.google.com/ns/1.0"> | ||
| <application>YOUR-PROJECTID-HERE</application> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lowercase will work better w/ new plugin.
| --> | ||
| <appengine-web-app xmlns="http://appengine.google.com/ns/1.0"> | ||
| <application>YOUR-PROJECTID-HERE</application> | ||
| <version>1</version> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No longer required.
jerjou
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay - I ran into a problem when actually deploying the app (plus the whole new-maven-plugin not working, which might be related), so working on that. But for the moment, I've pushed some updates to this PR.
appengine/firebase-tictactoe/pom.xml
Outdated
| <scope>provided</scope> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.json</groupId> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah - communicating with firebase is all through json
| <relativePath>../..</relativePath> | ||
| </parent> | ||
| <properties> | ||
| <objectify.version>5.1.13</objectify.version> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. I never really got the point of specifying the version numbers as a separate property, though, so if you don't know either, I can just specify it as a literal in the dependency section ;)
| <version>20160810</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>com.googlecode.objectify</groupId> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup - in this app, datastore is the canonical representation of the game board, and we use objectify to persist it.
appengine/firebase-tictactoe/pom.xml
Outdated
| <plugins> | ||
| <!-- Parent POM defines ${appengine.sdk.version} (updates frequently). --> | ||
| <plugin> | ||
| <groupId>com.google.appengine</groupId> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ugh... oddly enough, switching to the new plugin causes firebase calls to fail..
Updating for now. Working on getting it to work with the new plugin..
| public void doPost(HttpServletRequest req, HttpServletResponse resp) | ||
| throws IOException { | ||
| UserService userService = UserServiceFactory.getUserService(); | ||
| String gameId = req.getParameter("g"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dunno - this is what the python query parameter was. The channel sample alternated between calling it "g" and "gameKey".
shrug Updated to be more descriptive.
|
|
||
| String currentUserId = userService.getCurrentUser().getUserId(); | ||
| if (currentUserId.equals(game.getUserX()) || currentUserId.equals(game.getUserO())) { | ||
| /* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh yeah - I just commented this out in anticipation of filling it in later, but turns out this servlet isn't actually referenced anywhere so I'll just delete it.
| throws IOException { | ||
| UserService userService = UserServiceFactory.getUserService(); | ||
| String gameId = req.getParameter("g"); | ||
| int piece = new Integer(req.getParameter("i")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated to be more descriptive
| // 4. More general template values | ||
| req.setAttribute("game_key", gameKey); | ||
| req.setAttribute("me", userId); | ||
| req.setAttribute("channel_id", channelId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup - the "channel id" is now a unique identifier on the firebase side that the server posts updates to, and the client listens to for updates.
| <script> | ||
| // Initialize Firebase | ||
| var config = { | ||
| apiKey: "AIzaSyC3gJDK1r-8nZuJGdmXntJR4E8cqH8qSTk", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh whoops. Yes.
| // Initialize Firebase | ||
| var config = { | ||
| apiKey: "AIzaSyC3gJDK1r-8nZuJGdmXntJR4E8cqH8qSTk", | ||
| authDomain: "channels-api-deprecation.firebaseapp.com", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a README with setup steps.
|
|
||
| * If you see the error `Google Cloud SDK path was not provided ...`: | ||
| * Make sure you've installed the [Google cloud SDK][sdk] | ||
| * You may have installed it in a non-standard path. In that case, set the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point - you might mention that the Google Cloud SDK needs to be on your PATH, if not, you can fix it by adding...
| <relativePath>../..</relativePath> | ||
| </parent> | ||
| <properties> | ||
| <objectify.version>5.1.13</objectify.version> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No the repo-gardener requires it to be in properties, so thats a good thing.
appengine/firebase-tictactoe/pom.xml
Outdated
| <scope>provided</scope> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.json</groupId> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I typically use this as it's very easy to use, but consider Google GSON. I'm trying to switch.
| return firebaseSnippet.substring(openQuote + 1, closeQuote); | ||
| } | ||
|
|
||
| Game() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You might wish to set an ID here.
| // 4. More general template values | ||
| req.setAttribute("game_key", gameKey); | ||
| req.setAttribute("me", userId); | ||
| req.setAttribute("channel_id", channelId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should it be renamed?
| # Tic Tac Toe on Google App Engine Standard using Firebase | ||
|
|
||
| This directory contains a project that implements a realtime two-player game of | ||
| Tic Tac Toe on [Google App Engine] Standard, using the [Firebase] database |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd put Standard inside the link and link to https://cloud.google.com/appengine/docs/about-the-standard-environment instead
|
|
||
| ## Prerequisites | ||
|
|
||
| * Install [Apache Maven][maven] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
3.3.9 or later
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It used to be the case for the old appengine plugin, but I've been using mvn 3.0.5 with our plugin and it hasn't complained.
| * Install [Apache Maven][maven] | ||
| * Create a project in the [Firebase Console][fb-console] | ||
| * Install the [Google Cloud SDK][sdk] | ||
| * Download [service account credentials][creds] and move it to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
move them
|
|
||
| public class DeleteServlet extends HttpServlet { | ||
| @Override | ||
| public void doPost(HttpServletRequest req, HttpServletResponse resp) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
req --> request
resp --> response
since Google Java style avoids abbreviatiosn
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DevRel is a bit more relaxed on this.
| static { | ||
| try { | ||
| sFirebaseSnippet = CharStreams.toString( | ||
| new InputStreamReader(new FileInputStream(FIREBASE_SNIPPET_PATH))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
needs character encoding specified
| .setDatabaseUrl(sFirebaseDbUrl) | ||
| .build(); | ||
| FirebaseApp.initializeApp(options); | ||
| } catch (Exception e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
try to catch a more specfic exception if possible
| * OfyHelper, a ServletContextListener, is setup in web.xml to run before a JSP is run. This is | ||
| * required to let JSP's access Ofy. | ||
| **/ | ||
| public class OfyHelper implements ServletContextListener { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ObjectifyHelper
| import javax.servlet.http.HttpServletRequest; | ||
| import javax.servlet.http.HttpServletResponse; | ||
|
|
||
| public class MoveServlet extends HttpServlet { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some Javadoc for this class?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At least some comments, if not full JavaDoc
| import javax.servlet.http.HttpServletRequest; | ||
| import javax.servlet.http.HttpServletResponse; | ||
|
|
||
| public class OpenedServlet extends HttpServlet { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Javadoc for this class?
|
Pushed another iteration. Not all the comments were addressed yet - of note, I haven't figured out how to use Guice yet -_-; |
|
Take a look at the Objectify Guestbook in |
|
I'd recommend Dagger 2 over Guice. Guice is runtime dependency injection and dagger is compile-time (plus a lot easier to debug) |
|
Sigh - meant to review this AM. |
lesv
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PTAL
| * Install [Apache Maven][maven] 3.0.5 or later | ||
| * Create a project in the [Firebase Console][fb-console] | ||
| * Install the [Google Cloud SDK][sdk] | ||
| * For staging locally, you must supply credentials that would otherwise be |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"staging locally" -> "local development". gcloud auth application-default login is preferred to GOOGLE_APPLICATION_CREDENTIALS
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
gcloud auth application-default login doesn't work (again). I'm getting permission-denied when using it to make firebase calls from the server. I think it also doesn't provide a value for appIdentity.getServiceAccountName(), since it's not a service account. And I think it also can't sign the jwt, since it's not actually an RSA cert.
I changed it from "local development" to "staging locally" because I was afraid "local development" would be confused with like, just coding it locally (and deploying it remotely). What about - "If using the dev appserver, ..."?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah - that makes sense - so explain that. (It's a firebase "feature")
| 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. | ||
| * Edit | ||
| [`src/main/webapp/WEB-INF/appengine-web.xml`](src/main/webapp/WEB-INF/appengine-web.xml) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really isn't required anymore, I typically use the string no-longer-required, the next Java SDK release will let us delete the line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah - so it fails if the element isn't there, but then it ignores it. Yay.
appengine/firebase-tictactoe/pom.xml
Outdated
| <plugin> | ||
| <groupId>com.google.cloud.tools</groupId> | ||
| <artifactId>appengine-maven-plugin</artifactId> | ||
| <version>1.0.0</version> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Us a property variable here.
|
|
||
| public class DeleteServlet extends HttpServlet { | ||
| @Override | ||
| public void doPost(HttpServletRequest req, HttpServletResponse resp) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DevRel is a bit more relaxed on this.
| import java.util.HashMap; | ||
| import java.util.Map; | ||
|
|
||
| public class FirebaseChannel { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some comments about this class? Why is it here? You might also want to include our // [START blah] tags for interesting parts.
| return instance; | ||
| } | ||
|
|
||
| private FirebaseChannel() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
JavaDoc or at least some comments -- we are trying to teach.
| return message.toString(); | ||
| } | ||
|
|
||
| //[START send_updates] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some comments would be good, if you are expecting us to publish it.
| import javax.servlet.http.HttpServletRequest; | ||
| import javax.servlet.http.HttpServletResponse; | ||
|
|
||
| public class MoveServlet extends HttpServlet { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At least some comments, if not full JavaDoc
| thisUri.getScheme(), thisUri.getUserInfo(), thisUri.getHost(), | ||
| thisUri.getPort(), thisUri.getPath(), query, ""); | ||
| return uriWithOptionalGameParam.toString(); | ||
| } catch (URISyntaxException e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we aren't bothering to do something with it, why catch it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At the very least tell the user why they want to catch it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mm... because java won't compile it unless I do? :-) It's either that, or let it through and make all the callers deal with it...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can mention that your method throws URISyntaxEception at the top and it should compile.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately, it doesn't look like URISyntaxException is a RuntimeException, so all callers would have to deal with it :-/
| } | ||
| ofy.save().entity(game).now(); | ||
| } else { | ||
| game = new Game(userId, null, " ", true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All those spaces seem wrong for some reason, seems pointless to have more than one, HTML will compress it, who needs that many?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And yes, I'm sure it came to you that way, so, I'm find if you just understand why that's necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Haha :-) The spaces are a flat representation of the game board. ie there are nine blank spaces, to signify that each of the nine spaces of the tic-tac-toe board are empty and available to move to. As the game progresses, they'll be replaced by 'X' and 'O' characters.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Though you could mention that in a comment.
jerjou
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated - PTAL
| // 4. More general template values | ||
| req.setAttribute("game_key", gameKey); | ||
| req.setAttribute("me", userId); | ||
| req.setAttribute("channel_id", game.getChannelKey(userId)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think "channel_id" still conveys the meaning fairly well. It's still a communication channel, even if it's not using the channel api per se.
I dunno - do you have a strong preference for another name?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SGTM
| * Install [Apache Maven][maven] 3.0.5 or later | ||
| * Create a project in the [Firebase Console][fb-console] | ||
| * Install the [Google Cloud SDK][sdk] | ||
| * For staging locally, you must supply credentials that would otherwise be |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
gcloud auth application-default login doesn't work (again). I'm getting permission-denied when using it to make firebase calls from the server. I think it also doesn't provide a value for appIdentity.getServiceAccountName(), since it's not a service account. And I think it also can't sign the jwt, since it's not actually an RSA cert.
I changed it from "local development" to "staging locally" because I was afraid "local development" would be confused with like, just coding it locally (and deploying it remotely). What about - "If using the dev appserver, ..."?
| 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. | ||
| * Edit | ||
| [`src/main/webapp/WEB-INF/appengine-web.xml`](src/main/webapp/WEB-INF/appengine-web.xml) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah - so it fails if the element isn't there, but then it ignores it. Yay.
| thisUri.getScheme(), thisUri.getUserInfo(), thisUri.getHost(), | ||
| thisUri.getPort(), thisUri.getPath(), query, ""); | ||
| return uriWithOptionalGameParam.toString(); | ||
| } catch (URISyntaxException e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mm... because java won't compile it unless I do? :-) It's either that, or let it through and make all the callers deal with it...
| } | ||
| ofy.save().entity(game).now(); | ||
| } else { | ||
| game = new Game(userId, null, " ", true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Haha :-) The spaces are a flat representation of the game board. ie there are nine blank spaces, to signify that each of the nine spaces of the tic-tac-toe board are empty and available to move to. As the game progresses, they'll be replaced by 'X' and 'O' characters.
ce7de41 to
36c256b
Compare
|
LGTM |
This is my first pass at this. I copied the channel api sample to start, so there might be some vestigial stuff from that. I'm going to make another clean-up pass on this, but I figured I'd send it out sooner rather than later so you can start taking a look.
FYI this is on GAE standard, just 'cuz the channel api was on standard. Think it's worth it to put it on flex?