Why use DELETE/POST instead of PUT for 'unfollowing/following' a user? - api-design

Referencing this API tutorial/explanation:
https://thinkster.io/tutorials/design-a-robust-json-api/getting-and-setting-user-data
The tutorial explains that to 'follow a user', you would use:
POST /api/profiles/:username/follow.
In order to 'unfollow a user', you would use:
DELETE /api/profiles/:username/follow.
The user Profile initially possesses the field "following": false.
I don't understand why the "following" field is being created/deleted (POST/DELETE) instead of updated from true to false. I feel as though I'm not grasping what's actually going on - are we not simply toggling the value of "following" between true and false?
Thanks!

I think that the database layer have to be implemented in a slightly more complex way than just having a boolean column for "following".
Given that you have three users, what would it mean that one of the users has "following": true? Is that user following something? That alone cannot mean that the user is following all other users, right?
The database layer probably consists of (at least) two different concepts: users and followings; users contain information about the user, and followings specify what users follow one another.
Say that we have two users:
[
{"username": "jake"},
{"username": "jane"}
]
And we want to say that Jane is following Jake, but not the other way around.
Then we need something to represent that concept. Let's call that a following:
{"follower": "jane", "followee": "jake"}
When the API talks about creating or deleting followings, this is probably what they imagine is getting created. That is why they use POST/DELETE instead of just PUT. They don't modify the user object, they create other objects that represent followings.
The reason they have a "following": true/false part in their JSON API response is because when you ask for information about a specific user, as one of the other users, you want to know if you as a user follows that specific user.
So, given the example above, when jane would ask for information about jake, at GET /api/profiles/jake, she would receive something like this:
{
"profile": {
"username": "jake",
"bio": "...",
"image": "...",
"following": true
}
}
However, when jake would ask for the profile information about jane, he would instead get this response:
{
"profile": {
"username": "jane",
"bio": "...",
"image": "...",
"following": false
}
}
So, the info they list as the API response is not what is actually stored in the database about this specific user, it also contains some information that is calculated based on who asked the question.

Using a microPUT would certainly be a reasonable alternative. I don't think anybody is going to be able to tell you why a random API tutorial made certain design decisions. It may be that they just needed a contrived example to use POST/DELETE.
Unless the author sees this question, I expect it's unanswerable. It's conceivable that they want to store meta information, such as the timestamp of the follow state change, but that would be unaffected by POST/DELETE vs. PUT.

Related

Setting up 'Trigger Email' Firebase Extension

I learned about firebase and cloud functions recently and have been able to develop simple applications with them.
I now want to expand my knowledge and really struggling with Trigger Email Extension.
On a specific event on my firebase, I want to fire an email to the user in a custom format, but I am unable to even activate the extension for now.
Can someone please explain with example please about these fields marked in the picture?
I had this question too, but got it resolved. Here's your answer:
"Email documents collection" is the collection that will be read to trigger the emails. I recommend leaving named "mail" unless you already have a collection named mail.
"Users collection (Optional)" refers to a collection (if any) that you want to use in tandem with a user auth system. I haven't had this specific use case yet, but I imagine once you understand how Trigger Email operates, it should be somewhat self-explanatory.
"Templates collection (Optional)" is helpful for templates in which you can use handlebar.js is automatically input specific information per user. (eg. <p>Hello, {{first_name}}</p> etc.) Similar to the previously mentioned collections, you can name it whatever you want.
How to create a template (I have yet to actually implement this, so take this with a grain of salt):
In your templates collection, you want to name each document with a memorable ID. Firebase gives the example:
{
subject: "#{{username}} is now following you!",
html: "Just writing to let you know that <code>#{{username}}</code> ({{name}}) is now following you.",
attachments: [
{
filename: "{{username}}.jpg",
path: "{{imagePath}}"
}
]
}
...specifying a good ID would be following. As you can see, the documents should be structured just like any other email you would send out.
Here is an example of using the above template in javascript:
firestore()
.collection("mail")
.add({
toUids: ["abc123"], // This relates to the Users Collection
template: {
name: "following", // Specify the template
// Specify the information for the Handlebars
// which can also be pulled from your users (toUids)
// if you have the data stored in a user collection.
// Of course that gets more into the world of a user auth system.
data: {
username: "ada",
name: "Ada Lovelace",
imagePath: "https://path-to-file/image-name.jpg"
},
},
})
I hope this helps. Let me know if you have an issues getting this set up.

failed to keep user on the same prompt when they enter the wrong nunber?

we'd like to keep user on the same prompt when they enter the wrong number, we have tried anything_else, and "true", "jump to", but it messed up, please take a look at the attached to reproduce it, Thanks
pleae enter "how much"
if you enter 6, it will lock the prompt(I assign 1 to 5 to different identification), this behavior is correct .
then enter 2, we will get messed up...
please import this json to reproduce it, thanks
https://drive.google.com/file/d/0B1YdUMoS4l7ub1BZdUg1c1dQeG8/view?usp=sharing
The better form is use the entitie pre-defined by IBM, #sys-number to get numbers from the user input. And you can use use with conditions and to get the number with context variable too, check the JSON example:
{
"context": {
"number": "<? #sys-number ?>"
},
"output": {
"text": {
"values": [
"Now is $hora. Sector please?"
],
"selection_policy": "sequential"
}
}
}
If user type two or 2, the entitie recognize!
You can use regex expression to obtain only the numbers you have pre-defined too!
How to active: -> Entities -> System Entities -> sys-number = ON:
Obs.: Waiting Watson TRAINNING after you active this entitie.
Example, with sys-number add in your node condition:
#sys-number:1
Check the image:
If user type the number correct:
Check the dialog if user dont type the correct number with true condition:
I did the example for you understand what I do for that:
Download the JSON for verify how to do it with REGEX here.
Download the JSON for verify how to do it with SYS-NUMBER here.
EDIT:
Refer your questions
In this case you can use regex, and use the context variable for make conditions in other node. My workspace with regex can help you with numbers. And, the variable $number you can use in the next node to verify if the user typed correctly the number.
And, the other case is to use the Jump to inside conversation. And use true if the user dont type the number correctly again.
Check my image:
Download the new workspace here.
Study more about conditions here.

Watson Conversation: condition matching input to context array

Taking the car dashboard example, I altered the initial #genre node to be #genre:classical. I also added a list to the contex
"choices":["Beethoven","Mahler 9","Brahms 3rd"]
and the Watson response is "I have 3 selections". The condition on the next node is $choices.contains(input.text). The "Found a match" response is just for testing. It looks like this:
When I test this in the api tool and type "Beethoven" both "Found a match" and "Great choice!..." appear. Same for the other two choices, but only if I type the exact choice, e.g., "Mahler 9". Typing "Mahler" or "mahler" doesn't get a match. I read through the SpEL documentation but couldn't see a way in a one-line condition to parse through the list looking for partial matches.
So my question is, is there an condition expression that would match partial user input, e.g., "Mahler"? I'll be using the Java SDK to code the app server, so alternatively I wondered if I could add a temporary #entity just for this sequence instead of using the context list then delete it when the conversation is done? Or is there a way to construct a more complex condition in the MessageRequest and will Watson recognize it? Or is this just not the right way to go about this? Any pointers, examples or docs much appreciated.
So my question is, is there an condition expression that would match partial user input
You can't add temporary entities or intents. As adding them forces Watson to start training itself (even if you could it through code).
You can however create quite complex regular expressions, pass them in as a context variable.
For example your advanced node can have:
{
"output": {
"text": "Please ask me a question."
},
"context": {
"rx": "fish|[0-9]+"
}
}
Then in you condition you would write.
input.text.matches(context.rx)
This will then trigger if the person mentions a number, or the word fish. So you can create your partial user input checking that way.

Currently Using MySQL, Looking at DocumentDB

I currently use MySQL, after looking into Document DB it seems like it may be a good move. I do a TON (95%) of querying for single records. As my database gets larger, the time its taking to do this seems to be getting slower. Both reading and writing. I'm curious based on the (simplified) scheme below if it could be a good move to a DocumentDB, and what the layout would be for said schema (i'm a bit new to documentDB)
User
UserID
Username
CreatedDate
Tank
TankID
UserID REF User.UserID
TankName
Awards
Map
MapID
MapName
MapFIle
MapData
MapID REF Map.MapID
TankID REF Tank.TankID
Rank
Color
TimePlayed
Equipment
Everytime a player joins, the data from Tank,MapaData is Queried to gather a full tank object. Every time they die, win an award, kill somebody, or exit the game, the data is then written back out to tank,and mapdata.
The website queries the User table for login, which stores the username and a hash of the password. Once logged in the users are able to modify/delete/create new tanks on the website, which inserts records into the tank/mapdata tables.
The website also stores Top 25 in the World, t25 in map, t25 for each color, t25 for each color for each map.
That's about the only query patterns I can think of at this moment.
Based on the provided information you have the choice of several schema designs (with JSON as examples). I've made some assumptions, such as that more than one tank can be on one map and map data is only linked to a single map. You have to tweak it for your needs. I also try to provide some advantages and disadvantages of every solution.
Option #1 (Single collection)
This should be the easiest, but not the best solution. Here you put everything into one document with extreme "denormalization".
{
"mapname": "map1",
"mapfile": "mapfile1",
"data": {
"rank": "rank1",
"color": "color1",
...
"tanks": [
{
"name": "tank1",
...
"user": {
"name": "user1",
...
}
},
{
...
}
]
}
}
This solution works best when you do a lot of writes, rare updates and reads where you want to get all information together. On the other side it has a lot of disadvantages, such as storing user information directly into your application data (an example would be the password hash).
Option #2 (Two collections)
Put your user data into one collection and the other data into a second collection.
User collection
{
"id": 1,
"username": "user1",
"password": "passwordhash",
...
}
Data collection
{
"mapname": "map1",
"mapfile": "mapfile1",
"data": {
"rank": "rank1",
"color": "color1",
...
"tanks": [
{
"name": "tank1",
...
"user": userId
}
},
{
...
}
]
}
}
This option is a lot better than the first one. First you don't want to have sensitive user data (such as the hash of the password) in a collection with your other data. Also this works better for reads of the user object, because you just retrieve the information you need without skipping a lot of not needed fields. A disadvantage is that heavy write operations on the tank object can become a problem.
Option #3 (Three collections)
The next step could be to move the tanks out of the data collection into their own collection.
User collection
{
"id": 1,
"username": "user1",
"password": "passwordhash",
...
}
Tank collection
{
"name": "tank1",
...
"user": userId
}
Data collection
{
"mapname": "map1",
"mapfile": "mapfile1",
"data": {
"rank": "rank1",
"color": "color1",
...
"tanks": [
idOfTank1,
idOfTank2,
...
]
}
}
This works best for a lot of writes of single objects, such as the tanks, and reading tanks from their collection. This solution has its problems when reading a lot of data together, for example if you want to get a map and all the tanks in that map. In that case you have to resolve the dependencies of the tanks and the map data.
Summary
As seen, schema design is not easy in a document-oriented database. This is the reason why I asked for the query patterns. To come up with a good design you have to know most of the query patterns in advance. To get started, you should create a simple prototype with a design you think makes sense and test your query patterns with some test data. If that works, you can make minor changes to get even better performance. If not, rethink your query patterns and how a better design could look like. Keep in mind that you don't need a full-blown application for that. Most of that can be tested before a single line of code is written, for example with the administration shell of MongoDB or a simple console application in the case of DocumentDB.

ServiceStack proper way to access routes and avoid markup

I think this question is more about best practices regarding web services and not necessarily limited to ServiceStack only. From what I've read here and on the SS wiki, the 'recommended' way to implement parent-child entities is to break them down via routes.
For example:
/Users/{UserID}
/Users/{UserID}/Entities
Where User is the logged on user, and entities are his/her items. I'm implementing jqueryui autocomplete and here is where I'm suspecting I'm not doing the right thing.
In the script the path needs the Userid, so I have to manually render it in the browser so that it reads:
type: "GET",
url: "svc/users/**8**/entities",
data: { "SearchTerm": request.term, "Format": 'json' },
This smells wrong to me. I have the UserID from the session and I can get it that way. So I wonder if there a better way to access these objects without having to render data directly into markup?
Am I doing this wrong?
On a side note: I know I could place this data in a hidden field and access it via script etc, I am just curious if there is a better/recommended way to do this via sessions while keeping the routes as is.
Generally this is done with another endpoint, Facebook for instance, uses /my/, but you could do what ever you want.
The reason being, it's very likely you will be returning different information for a user about themselves than you share about that user with someone else.
Let's pretend /user/{UserId}/books returns a user's favorite books. If I want to know what someone's favorite books are, I might be interested in the title, and a brief description, but if I want to see (and possibly manage) my list of favorite books then I might want more information, like the day I added the favorite book, or friends of mine that also like the book.
so /user/{UserId}/books returns:
{
"books":[
{ "title":"Hary Potter", "desc":"A boy who is magic..." }
]
}
however /my/books returns:
{
"books":[
{
"title":"Harry Potter",
"desc":"A boy who is magic...",
"friensWhoLikeBook":[
{ "id":1234, "name":"Bob" }
],
"personalCommentsAboutBookNotToBeShared":"This book changed my life..."
}
]
}

Resources