Includes
Includes are a mechanism in SocialStack which allow you to request additional content when obtaining an entity (or a list of entity) from the API. For example, you have a loop of things and would like to also display the name of the user who created each one. They are not case sensitive.
Frontend Usage
Typically includes are used from either Loop or Content:
import Loop from 'UI/Loop';
...
<Loop over='page/list' includes={['creatorUser']}>
{page => {
return <div>
The page "{page.title}" was created by {page.creatorUser.username}
</div>;
}}
</Loop>
import Content from 'UI/Content';
...
<Content type='page' id={1} includes={['creatorUser']}>
{page => {
if(!page){
// Just an example. The page might also not exist. See <Content> usage for more info.
return 'Loading..';
}
return <div>
The page "{page.title}" was created by {page.creatorUser.username}
</div>;
}}
</Content>
If you aren't in the React render scope, you can specify includes via a webRequest as well:
webRequest('page/1', null, {includes: ['creatorUser']});
API Usage
The frontend is actually just adding any includes on as a GET parameter, like so:
https://www.example.com/v1/page/1?includes=creatorUser
Nested Includes
You will often have situations where you want to include things on the included objects as well. This is done with a dot:
['creatorUser', 'creatorUser.tags']
Show me everything
You can wildcard include to show all available current level includes, and this is available nested as well:
['*', 'creatorUser.*']
Note that the wildcard is only supported as the last field (*.creatorUser is not available for example, as not everything necessarily supports the named field you want).
Making something includable
Includable things are either singular objects (like the creator user - a piece of content only ever has 1 of those), or a list of things.
Single objects
To make a single object, you'll need to create a "virtual field" on your C# type:
[HasVirtualField("OtherTypeName", typeof(OtherTypeName), "OtherTypeNameId")]
public partial class MyCustomType : VersionedContent<uint>
{
public uint OtherTypeNameId;
}
You can declare as many virtual fields as you need, but essentially you define:
- The virtual fields name (this is the text which goes inside the includes array)
- The type of thing that will be included. This must always be a Content type.
- The name of the field where the ID for your included thing will come from.
For example, this is how creatorUser is actually defined:
[HasVirtualField("CreatorUser", typeof(User), "UserId")]
"Create an includable field called CreatorUser, which will be a User type, and the ID of the user to include comes from the UserId column".
Lists
To include a list of things on a type (for example, a list of answers on a poll), you'll need to mark the C# type as listable:
[ListAs("MyCustomTypes")]
public partial class MyCustomType : VersionedContent<uint>
{
public uint OtherTypeNameId;
}
You can use multiple ListAs declarations. Users is a good example of a use case for this - there is a list of permitted users on other types, but also you might want a list of members etc.
Permissions
Includes are a powerful way to get simple secondary permissions. For example, lets say you're permitted to see messages in a chat if you are a member of the chat. We can structure this such that the user has a userPermit in the chat, with chat list/load access restricted by the custom rule HasUserPermit(), and we can restrict message list/load with the rule IsIncluded(). This means a message can only be displayed if you arrived on it via an include, and you can only load the chat (and thus include its messages) if you have a permit.