KeystoneJS and naming? - keystonejs

I'm new to Keystone JS and NodeJS.
This is the Part I totally do not understand;
Example 'Post' as defined as 'Post', but there are no 'posts', but when I call/ search for Post, in example (and my practices), it was 'posts'.
Exp:
keystone.set('nav', {
posts: ['posts', 'post-categories'],
enquiries: 'enquiries',
users: 'users',
});
Similar 'PostCategory' => 'post-categories', 'Enquiry'=>'enquiries' etc.
But when I making new Routes=>View for my custom post type, I must use:
locals.data = {
food: []
};
At this, its 'food' not 'foods'.

Keystone automatically uses the plural form of your model names in the Admin Panel, instead of its singular name. It's still referred to by its singular name (Food, PostCategory, Enquiry, etc.) throughout your code, but the admin panel uses the plural forms if referring to multiple documents of a model.
When working with local, you can name the properties of that object anything you want. Doesn't have to be locals.data.food; it can be whatever you want.
Also, the plural form of food is food. So nothing will change when using the plural form of a Food model in your Admin Panel.

Related

Error trying to reorder items within another list in Keystone 6

I'm using KeystoneJS v6. I'm trying to enable functionality which allow me to reorder the placement of images when used in another list. Currently i'm setting up the image list below, however I'm unable to set the defaultIsOrderable to true due to the error pasted.
KeystoneJS list:
Image: list({
fields: {
title: text({
validation: { isRequired: true },
isIndexed: 'unique',
isFilterable: true,
isOrderable: true,
}),
images: cloudinaryImage({
cloudinary: {
cloudName: process.env.CLOUDINARY_CLOUD_NAME,
apiKey: process.env.CLOUDINARY_API_KEY,
apiSecret: process.env.CLOUDINARY_API_SECRET,
folder: process.env.CLOUDINARY_API_FOLDER,
},
}),
},
defaultIsOrderable: true
}),
Error message:
The expected type comes from property 'defaultIsOrderable' which is declared here on type 'ListConfig<BaseListTypeInfo, BaseFields<BaseListTypeInfo>>'
Peeking at the definition of the field shows
defaultIsOrderable?: false | ((args: FilterOrderArgs<ListTypeInfo>) => MaybePromise<boolean>);
Looking at the schema API docs, the defaultIsOrderable lets you set:
[...] the default value to use for isOrderable for fields on this list.
You're trying to set this to true but, according to the relevant section of the field docs, the isOrderable field option already defaults to true.
I believe this is why the defaultIsOrderable type doesn't allow you to supply the true literal – doing so would be redundant.
So that explains the specific error your getting but I think you also may have misunderstood the purpose of the orderBy option.
The OrderBy Option
The field docs mention the two effects the field OrderBy option has:
If true (default), the GraphQL API and Admin UI will support ordering by this field.
Take, for example, your Image list above.
As the title field is "orderable", it is included in the list's orderBy GraphQL type (ImageOrderByInput).
When querying the list, you can order the results by the values in this field, like this:
query {
images (orderBy: [{ title: desc }]) {
id
title
images { publicUrl }
}
}
The GraphQL API docs have some details on this.
You can also use the field to order items when listing them in the Admin UI, either by clicking the column heading or selecting the field from the "sort" dropdown:
Note though, these features order items at runtime, by the values stored in orderable fields.
They don't allow an admin to "re-order" items in the Admin UI (unless you did so by changing the image titles in this case).
Specifying an Order
If you want to set the order of items within a list you'd need to store separate values in, for example, a displayOrder field like this:
Image: list({
fields: {
title: text({
validation: { isRequired: true },
isIndexed: 'unique',
isFilterable: true,
}),
displayOrder: integer(),
// ...
},
}),
Unfortunately Keystone doesn't yet give you a great way to manage this the Admin UI (ie. you can't "drag and drop" in the list view or anything like that). You need to edit each item individually to set the displayOrder values.
Ordering Within a Relationship
I notice your question says you're trying to "reorder the placement of images when used in another list" (emphasis mine).
In this case you're talking about relationships, which changes the problem somewhat. Some approaches are..
If the relationship is one-to-many, you can use the displayOrder: integer() solution shown above but the UX is worse again. You're still setting the order values against each item but not in the context of the relationship. However, querying based on these order values and setting them via the GraphQL API should be fairly straight forward.
If the relationship is many-to-many, it's similar but you can't store the "displayOrder" value in the Image list as any one image may be linked to multiple other items. You need to store the order info "with" the relationship itself. It's not trivial but my recent answer on storing additional values on a many-to-many relationship may point you in the right direction.
A third option is to not use the relationship field at all but to link items using the inline relationships functionality of the document field. This is a bit different to work with - easier to manage from the Admin UI but less powerful in GraphQL as you can't traverse the relationship as easily. However it does give you a way to manage a small, ordered set of related items in a many-to-many relationship.
You can save an ordered set of ids to a json field. This is similar to using a document field but a more manual.
Hopefully that clears up what's possible with the current "orderBy" functionality and relationship options. Which of these solutions is most appropriate depends heavily on the specifics of your project and use case.
Note too, there are plans to extend Keystone's functionality for sorting and reordering lists from both the DX and UX perspectives.
See "Sortable lists" on the Keystone roadmap.

Is this a valid REST API?

I am designing an API.
There's the user profile, accessible at
http://example.org/api/v1/users (resp. http://example.org/api/v1/users/:id)
Now, the user's profile will be dynamic.
So we will allow an API function to add a new profile attribute.
Is the following a valid REST API URL for this?
POST http://example.org/api/v1/users/attributes
Indeed, to retrieve a specific user, the user's id would be appended to the .../users/ URL.
Now if I use the "attributes" element after /users/, would that somehow break the user id pattern for the URL?
I'd like to keep the base URL to be api/v1/users though, because logically I am modifying the users profile still...
EDIT: The attributes would be added valid for all profiles, it's independent of a user. Say the profile has "name", "surname", "email", and I want to add "address" to all profiles (Of course I know that users with a missing "address" field would not get the new attribute)
What is a good practice to address such an issue?
I think the id should be kept in the URL because you are adding the attributes to a specific user, right?
It is an acceptable solution to use the /api/v1/users/attributes as long as the :id cannot be the text: "attributes". However I recommend to create your own media type, microformat, or microdata for the attributes, because it is rather a type than a resource.
I think you should check these links:
http://alps.io/spec/index.html
http://www.markus-lanthaler.com/hydra/spec/latest/core/
http://schema.org/
http://microformats.org/wiki/microformats2
http://amundsen.com/media-types/maze/
If the user can set what attributes she can have, only then should you use a resource for attributes. But then each user should have one. But I don't think using resources will be necessary, microdata and microformats both contain more than enough person description attributes...
Some update after 5 months:
Now if I use the "attributes" element after /users/, would that
somehow break the user id pattern for the URL?
From the perspective of the client that "id pattern" does not exist. The client follows links by checking the semantics annotated to them. So REST clients are completely decoupled from the URI structure of the actual REST API (aka. uniform interface constraint). If your pattern breaks, then it is completely a server side, link generation and routing issue, which is not a client side concern.
Say the profile has "name", "surname", "email", and I want to add
"address" to all profiles. What is a good practice to address such an
issue?
Address is an optional field in this case and probably a sub-resource, because it can have further fields, like city, postal code, street, etc... You can add address separately, for example with PUT /users/123/address {city: "", street: "", ...} or you can add those fields to your user form, and add a partial update to the user, like PATCH /users/123 {address: {city: "", street: "", ...}} if only the address changes.
In case you want to update every resource in the entire collection I would send a PATCH request to /users.
While it is a valid URI, I would suggest avoiding POST http://example.org/api/v1/users/attributes. In my opinion, it violates the principle of least surprise when a collection endpoint has a child node which is not a member of the collection. If you want to track user attributes as shared by all users, then that's a separate collection, perhaps /user-attributes.
POST /user-attributes
{
"name": "Email Address",
"type": "String",
...
}
GET /user-attributes would return all the possible attributes, and GET /user-attributes/{id} would return all the metadata around an attribute.
If there's no metadata, then #inf3rno's suggestion to just PUT the attribute up and let the server deal with it is definitely worth considering.
This all presupposes you need to manage attributes through the API. If not, I agree with #inf3rno that media types are the way to go. Of course, in that case you may want a media type for the user-attributes resource ..

Facebook open graph custom stories and custom objects with js sdk

I'm trying to devellop a facebook app using custom stories and objects using js sdk but i met some problems.
What i try to achive is to create a story like:
"John played football on MyApp.- with Mike and Kate at city stadium"
under the text to be an image, a title and some description and on click goes to a link.
I have defined my action "Play" and my object "Football".
The action code facebook gives is:
FB.api(
'me/myapp:play',
'post',
{
football: "http://samples.ogp.me/xxxxxxxxxxxx"
},
function(response) {}
);
The object code facebook gives is:
FB.api(
'me/objects/myapp:football',
'post',
{
app_id: myappid,
type: "myapp:football",
url: "http://samples.ogp.me/xxxxxxxxxxx",
title: "Sample Football",
image: "https://fbstatic-a.akamaihd.net/images/devsite/attachment_blank.png",
description: ""
},
function(response) {}
);
However I used it as follows:
HTML:
<a onclick="playFootball();" href="#">Play Football</a>
JS:
function playFootball() {
FB.api(
'me/testapp-radu:play',
'post',
{
tags: "xxxxxx, xxxxxx,",
place: "https://www.facebook.com/pages/MyPlace/xxxxxx?ref=br_rs",
football: "http://samples.ogp.me/xxxxxx",
image: "http://www.peter-ould.net/wp-content/uploads/soccer-ball.jpg",
privacy: {'value': 'SELF'}
},
function(response) {});
}
and the result was this:
http://img545.imageshack.us/img545/337/yj9o.jpg
I was unable to change "played a football" into "played football".
The title was unchanged despite use of property title, same applies to url and description, also I tried to use an object but still no results.
If someone could help me solve these 2 problems I wold be really gratefull, or at least point me to some basic tutorials (facebook documentation sometimes is confuzing for me).
I think you are using football as a wrong way of specifying your story. You are using name of the instance instead of class. Let me explain.
Following are the two scenarios I could come up with:
Your app is about games that users play. So in this case your object becomes game and football will be the name of the game. So now your story reads like:
"A played football with B and C via your_app" since you will be providing the name property of the game object, facebook will select the right story sentence for you
Your app is about sports products, in which case your object becomes sport_product or product and football will be an instance of sport product. But in this case an action of play does not make any sense. So lets say buy is an action. So now your story reads like:
"A bought football with B and C via your_app"
Also if you think about it football does not make sense as an object. What would be the value of the name property of football, "Kaka's football" - does not make sense. Think in terms of a generic class of object rather an instance. (Think Movie not Inception).
Sample Story
Hope this makes sense!

Arbitrarily nesting some attributes in rabl

I'm designing a new API for my project, and I want to return objects that have nested children as json. For that purpose i've decided to use RABL.
I want the client side to be able to understand whether the object is valid, and if not which fields are missing in order to save it correctly.
The design I thought of should include some fields as optional, under an optional hash, and the rest are required. The required fields should appear right under the root of the json.
So the output I try to describe should look something like this:
{
"name": "John",
"last_name": "Doe",
"optional": {
"address": "Beverly Hills 90210",
"phones":[{"number":"123456","name":"work"}, {"number":"654321","name":"mobile"}]
}
}
The above output example describes the required fields name and last name, and the not required address and phones (which is associated in a belongs_to-has_many relationship to the object). name, last_name and address are User's DB fields.
Playing with RABL I didn't manage so far to create this kind of structure.
Any suggestions? I'm looking for a DRY way to implement this for all my models.
RABL is really good in creating JSON structures on the fly, so I don't see why you couldn't achieve your goal. Did you try testing if a field is set to null-able in the schema, and thus presenting it as optional? It seems a good approach for me. For the nested children, just do the same, but extend the template for the children.
For example, in your father/show.rabl display a custom node :optional with all the properties that can be null.
Then, create a child/show.rabl with the same logic. Finally, go back to father/show.rabl and add a child node, extending the child/show.rabl template. This way you could achieve unlimited levels of "optionals".
Hope it helped you.
In this case I'd use the free form option.
From https://github.com/nesquena/rabl
There can also be odd cases where the root-level of the response
doesn't map directly to any object.
In those cases, object can be assigned to 'false'
and nodes can be constructed free-form.
object false
node(:some_count) { |m| #user.posts.count }
child(#user) { attribute :name }

What is the proper RESTful way to "like" something in Rails 3?

Let's say I have a Rails 3 app that displays videos. The user can "Like" or "Dislike" the videos. Also, they can like/dislike other things like games. I need some help in the overall design and how to handle the RESTful routes.
Currently, I have a Like Class that uses polymorphic design so that objects are "likeable" (likeable_id, likeable_type)
I want to do this via AJAX (jQuery 1.5). So I was thinking something like:
javascript
// these are toggle buttons
$("likeVideo").click( function() {
$.ajax({
url: "/likes/video/" + video_id,
method: "POST",
....
});
} );
$("likeGame").click( function() {
$.ajax({
url: "/likes/game/" + game_id,
method: "POST",
....
});
} );
rails controller
Class Likes < ApplicationController
def video
# so that if you liked it before, you now DON'T LIKE it so change to -1
# or if you DIDN'T like it before, you now LIKE IT so change to 1
# do a "find_or_create_by..." and return JSON
# the JSON returned will notify JS if you now like or dislike so that the
# button can be changed to match
end
def game
# same logic as above
end
end
Routes
match "/likes/video/:id" => "likes#video", :as => :likes_video
match "/likes/game/:id" => "likes#game", :as => :likes_game
Does this logic seem correct? I am doing a POST via AJAX. Technically, shouldn't I be doing a PUT? Or am I being too picky over that?
Also, my controller uses non-standard verbs. Like video and game. Should I worry about that? Sometimes I get confused on how to match up the "correct" verbs.
An alternative would be to post to something like /likes/:id with a data structure that contains the type (game or video). Then I could wrap that in one verb in the controller...maybe even Update (PUT).
Any suggestions would be appreciated.
Rest architectural style does not specify which "verb" you should be using for what. It simply says that one can use HTTP if they want to for connectors.
What you are looking for is HTTP specifications for method definitions. In particular POST is intended for:
- Annotation of existing resources;
- Posting a message to a bulletin board, newsgroup, mailing list,
or similar group of articles;
- Providing a block of data, such as the result of submitting a
form, to a data-handling process;
- Extending a database through an append operation.
while PUT:
requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server.
Which category your functionality falls into is up to you - as long as you are consistent with yourself about it.