-
Notifications
You must be signed in to change notification settings - Fork 0
Implemented deletion of comments via Datastore #10
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
base: post-comments
Are you sure you want to change the base?
Conversation
…ment limiting input box
ccondit
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.
Looks like this PR is adding all of the functionality from previous steps (not just delete). Is the parent branch correct?
portfolio/pom.xml
Outdated
| <dependency> | ||
| <groupId>com.google.appengine</groupId> | ||
| <artifactId>appengine-api-1.0-sdk</artifactId> | ||
| <version>1.9.59</version> | ||
| </dependency> |
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.
nit - keep these dependencies alphabetical by 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.
Ok I'll adjust that.
| HashMap<String, String> messages = new HashMap<String, String>(); | ||
| // ArrayList<String> messages = new ArrayList<String>(); | ||
| static DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); | ||
| static List<Key> keys = new ArrayList<>(); |
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.
Before going further here - what will happen if the server restarts but the data store stays persisted? We'll lose this list of keys. Can you look into another way to get the keys? Possibly through the persistence layer?
https://cloud.google.com/appengine/docs/standard/java/datastore/retrieving-query-results
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.
Ok I'll do that.
|
The first three commits in this pull request were on the week3 branch I believe, and they were approved for merging in pull request #4. I didn't merge them into master, instead I had the mega branch last-steps-week3 built on top of those commits. The last six commits are the only new content, and they are all datastore/deletion based. I believe it most correctly extends post-comments since the last six commits are built on the commits in post-comments. |
| public class DataServlet extends HttpServlet { | ||
| static DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); | ||
| static List<Key> keys = new ArrayList<>(); | ||
| DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); |
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.
| DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); | |
| private final DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); |
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.
Ok.
| import javax.servlet.http.HttpServletRequest; | ||
| import javax.servlet.http.HttpServletResponse; | ||
|
|
||
| /** Servlet that returns some example content. TODO: modify this file to handle comments data */ |
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.
nit - update this comment to remove the TODO (since it's done!)
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.
Ok.
| List<Key> keys = DataServlet.keys; | ||
| datastore.delete(keys); | ||
| DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); | ||
| Query query = new Query("Comment").setKeysOnly(); |
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.
Are you missing an import here for Query?
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.
Oops I was.
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.
Note that there are more missing imports here. (DatastoreServiceFactory, for instance).
Try invoking mvn compile to ensure that your PR is building as expected.
| List<Key> keys = DataServlet.keys; | ||
| datastore.delete(keys); | ||
| DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); | ||
| Query query = new Query("Comment").setKeysOnly(); |
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.
Just a note that we'll probably want to find a common place to keep these constants (like "Comment") so that if they need to change, they will only need to be changed in one spot. No need to do anything now - just something to think about...
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.
Yea that could be a static class member later.
| import java.util.LinkedHashMap; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
| import java.util.Query; |
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 don't think this class exists. And I think you meant to add
import com.google.appengine.api.datastore.Query; to the DeleteServlet.
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 I've been gettting lazy whoops, I really need to compile before committing.
| List<Key> keys = DataServlet.keys; | ||
| datastore.delete(keys); | ||
| DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); | ||
| Query query = new Query("Comment").setKeysOnly(); |
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.
Note that there are more missing imports here. (DatastoreServiceFactory, for instance).
Try invoking mvn compile to ensure that your PR is building as expected.
| DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); | ||
| Query query = new Query("Comment").setKeysOnly(); | ||
| PreparedQuery keys = datastore.prepare(query); | ||
| datastore.delete(keys.asIterable()); |
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.
DatastoreService.delete takes a Key, not an Entity. Try something like:
PreparedQuery preparedQuery = datastore.prepare(query);
Iterator<Entity> entities = preparedQuery.asIterator();
while (entities.hasNext()) {
Key key = entities.next().getKey();
datastore.delete(key);
}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 ok, I will do that then.
| HashMap<String, String> messages = new HashMap<String, String>(); | ||
| // ArrayList<String> messages = new ArrayList<String>(); | ||
| private final DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); | ||
| private int maxNumComments; |
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 we initialize this with a default value in case doGet is executed before doPost?
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.
Sure, that's a good idea.
| } catch (NumberFormatException e) { | ||
| System.err.println("Could not convert to int: " + maxNumCommentsString); | ||
| System.err.println("Using default value of 7."); | ||
| maxNumComments = 7; |
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.
Use a constant for the default value? It would be it easier to read if it ever needs to be changed.
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.
Sure, I will do that.
| } | ||
|
|
||
| @Override | ||
| public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { |
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.
This post is doing two things now. It is used to post a comment and also to set the configuration property maxNumComments. Have you consider using a different Servlet to post configuration? Maybe it is overkilled, thoughts?
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 each of the two things it's doing are relatively small though, especially setting the configuration property. For this example it's probably overkill, but in the future I'll be careful to scope the post method carefully.
| } | ||
|
|
||
| Entity commentEntity = new Entity("Comment"); | ||
| commentEntity.setProperty("username", username); |
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.
Constants like "Comment", "username", "message" and "timestamp" are used multiple times. Consider defining a constant for those to make it less error prone.
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.
That's a good point, I'll consider it
| import javax.servlet.http.HttpServletResponse; | ||
|
|
||
| /** Servlet that deletes data from the database. */ | ||
| @WebServlet("/delete-data") |
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 personally am not a fan of this, but Servlets do have a doDelete method: https://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServlet.html#doDelete-javax.servlet.http.HttpServletRequest-javax.servlet.http.HttpServletResponse-
In REST API for example methods are used like this:
doPost - to create a new resource
doPut - to update a resource
doGet - to get a resource
doDelete - to delete a resource
If you are interested instead of defining a new servlet to delete you could simply override doDelete() method.
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.
That's interesting, I didn't know that, I'll keep that in mind for later.
| import javax.servlet.http.HttpServletResponse; | ||
|
|
||
| /** Servlet that deletes data from the database. */ | ||
| @WebServlet("/delete-data") |
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.
Have you consider instead of using 'data' in your Servlet path using a more specific term? For example message.
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 didn't consider that, I shall change it.
| public class DataServlet extends HttpServlet { | ||
| private final DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); | ||
| private int maxNumComments; | ||
| private final int defaultMaxNumComments = 7; |
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.
nit - per style guide, constants should be UPPER_CASE.
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 I didn't know that, I'll fix that.
| private final DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); | ||
| private int maxNumComments; | ||
| private final int defaultMaxNumComments = 7; | ||
| private int maxNumComments = defaultMaxNumComments; |
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 you'll want to leave this variable declaration in the doGet method, otherwise you'll risk a race condition where user 1 gets user 2's max number of comments.
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.
Ok, I'll move that.
| System.err.println("Could not convert to int: " + maxNumCommentsString); | ||
| System.err.println("Using default value of 7."); | ||
| maxNumComments = 7; | ||
| System.err.println("Using default value of " + defaultMaxNumComments + "."); |
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 need to change anything now, but when the time comes for logging, we'll want to use Java.util.logging, per the app engine guide - just a heads up for the capstone.
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.
Ok good to know.
| import com.google.gson.Gson; | ||
|
|
||
| /** Servlet that returns some example content. TODO: modify this file to handle comments data */ | ||
| /** Servlet that returns some example content.*/ |
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.
nit: this javadoc now needs to be updated to reflect the new features.
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 yea I forgot about that, I'll get to it.
| HashMap<String, String> messages = new HashMap<String, String>(); | ||
| // ArrayList<String> messages = new ArrayList<String>(); | ||
| private final DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); | ||
| private final int defaultMaxNumComments = 7; |
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.
this could be a static constant:
| private final int defaultMaxNumComments = 7; | |
| private static final int DEFAULT_MAX_NUM_COMMENTS = 7; |
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.
Ok
| // ArrayList<String> messages = new ArrayList<String>(); | ||
| private final DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); | ||
| private final int defaultMaxNumComments = 7; | ||
| private int maxNumComments = defaultMaxNumComments; |
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.
Note that since you're defining this field at the class level, when a user changes the number, it'll affect all users using your website. If you only want these comment num changes to affect individual users you should use session-scoped attributes.
For example here you would call request.getSession().setAttribute("maxNumComments", maxNumComments);. And to read it back you call (int)request.getSession().getAttribute("maxNumComments");.
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 ok, I'll do that then.
| System.err.println("Could not convert to int: " + maxNumCommentsString); | ||
| System.err.println("Using default value of " + defaultMaxNumComments + "."); |
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.
An alternative approach to format strings is to use the String.format java method. Here are some examples. It'll give you more control and in some cases it's less error prone.
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.
Ok, I'll adjust the code to use String.format.
| import com.google.gson.Gson; | ||
|
|
||
| /** Servlet that returns some example content. TODO: modify this file to handle comments data */ | ||
| /** Servlet that adds/retrieves comments stored in a Datastore*/ |
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.
nit - missing a space before the closing comment marker.
| /** Servlet that adds/retrieves comments stored in a Datastore*/ | |
| /** Servlet that adds/retrieves comments stored in a Datastore */ |
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.
Ok I wasn't aware, I'll fix that.
| System.err.println("Could not convert to int: " + maxNumCommentsString); | ||
| System.err.println("Using default value of " + defaultMaxNumComments + "."); | ||
| maxNumComments = defaultMaxNumComments; | ||
| System.err.format("Could not convert to int: %d%n", maxNumCommentsString); |
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.
there's a mismatch here since maxNumCommentsString is a string. s/%d/%s/.
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.
Ok, I'll do that replacement.
| Entity commentEntity = new Entity("Comment"); | ||
| commentEntity.setProperty("username", username); | ||
| commentEntity.setProperty("message", message); | ||
| commentEntity.setProperty("timestamp", System.currentTimeMillis()); |
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.
One notable omission here is that the comment is never added to the datastore. Do you want to add that here?
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.
That would be useful for the function of the website, so I'll add it.
| Query query = new Query("Comment").addSort("timestamp", SortDirection.DESCENDING); | ||
| PreparedQuery results = datastore.prepare(query); | ||
|
|
||
| int maxNumComments = (int)request.getSession().getAttribute("maxNumComments"); |
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.
Just another note here - that the session won't have this attribute on your first page load.
no need to update now, but something to ponder for future work.
Two deletion features exist: one simply limits how many comments appear at a time, and the other clears all comments made.