<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-GB">
	<id>https://wiki.socialstack.dev/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=72.201.192.210</id>
	<title>SocialStack - User contributions [en-gb]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.socialstack.dev/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=72.201.192.210"/>
	<link rel="alternate" type="text/html" href="https://wiki.socialstack.dev/index.php?title=Special:Contributions/72.201.192.210"/>
	<updated>2026-10-16T02:38:40Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.36.2</generator>
	<entry>
		<id>https://wiki.socialstack.dev/index.php?title=Filters&amp;diff=257</id>
		<title>Filters</title>
		<link rel="alternate" type="text/html" href="https://wiki.socialstack.dev/index.php?title=Filters&amp;diff=257"/>
		<updated>2022-08-11T20:12:38Z</updated>

		<summary type="html">&lt;p&gt;72.201.192.210: /* Filter functions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Filters are a query-like mechanism for filtering out results from a list endpoint. At their core, they are textual and support a set of arguments.&lt;br /&gt;
&lt;br /&gt;
= Filter examples =&lt;br /&gt;
&lt;br /&gt;
A textual filter with arguments is like so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
(UserId=? or UserId=?) and CreatedUtc&amp;gt;?&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It is not case sensitive. Filters in the permission system do not support these ? arguments (as it wouldn&amp;#039;t know where to bind the value from).&lt;br /&gt;
&lt;br /&gt;
Typically you&amp;#039;ll see this textual form of filter inside a Where() call in the C# API:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
uint userId = 14;&lt;br /&gt;
&lt;br /&gt;
var listOfThingsByThisUser = await Where(&amp;quot;UserId=?&amp;quot;).Bind(userId).ListAll(context);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
.Bind() is used to bind the actual value of an argument. Values are bound in this way such that the filter string is a constant and can benefit from being precompiled. Indeed, the whole filter system is built to be entirely non-allocating, as the Bind calls internally set your provided value onto an compiled object field, with that object originating from a pool for the given cached filter.&lt;br /&gt;
&lt;br /&gt;
= JS filter notation =&lt;br /&gt;
&lt;br /&gt;
Filters can also be constructed by the frontend, most often by [[Loop]]. In this scenario, a convenience filter notation is also available - it&amp;#039;ll take the object notation you provide, and then convert it into the textual format with arguments. Here&amp;#039;s some examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;js&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    where: {&lt;br /&gt;
        userId: 14&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[#Operators]] can be used by passing an object:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;js&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    where: {&lt;br /&gt;
        userId: {&lt;br /&gt;
            greaterThan: 14&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or statements are represented with an array:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;js&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    where: [{&lt;br /&gt;
        userId: 14&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
        userId: 16&lt;br /&gt;
    }]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And statements are performed by providing multiple field values:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;js&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    where: {&lt;br /&gt;
        userId: 14,&lt;br /&gt;
        createdUtc: {&lt;br /&gt;
            greaterThan: yesterday /* yesterday is a JS Date */&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you need more expressive power, you can also directly specify the query and args:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;js&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    query: &amp;quot;UserId=?&amp;quot;,&lt;br /&gt;
    args: [14]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Operators =&lt;br /&gt;
&lt;br /&gt;
Filters support a variety of operators for comparisons:&lt;br /&gt;
&lt;br /&gt;
* Greater than&lt;br /&gt;
  * Field&amp;gt;?   (query notation)&lt;br /&gt;
  * {greaterThan: x}    (JS notation)&lt;br /&gt;
&lt;br /&gt;
* Less than&lt;br /&gt;
  * Field&amp;lt;?   (query notation)&lt;br /&gt;
  * {lessThan: x}    (JS notation)&lt;br /&gt;
&lt;br /&gt;
* Greater than or equal&lt;br /&gt;
  * Field&amp;gt;=?   (query notation)&lt;br /&gt;
  * {greaterThanOrEqual: x}    (JS notation)&lt;br /&gt;
  * {geq: x}    (JS notation)&lt;br /&gt;
&lt;br /&gt;
* Less than or equal&lt;br /&gt;
  * Field&amp;lt;=?   (query notation)&lt;br /&gt;
  * {lessThanOrEqual: x}    (JS notation)&lt;br /&gt;
  * {leq: x}    (JS notation)&lt;br /&gt;
&lt;br /&gt;
* Equals&lt;br /&gt;
  * Field=?   (query notation)&lt;br /&gt;
  * x    (JS notation)&lt;br /&gt;
  * {equals: x}    (JS notation)&lt;br /&gt;
&lt;br /&gt;
* Not&lt;br /&gt;
  * Field!=?   (query notation)&lt;br /&gt;
  * !( .... )   (query notation)&lt;br /&gt;
  * {not: x}    (JS notation)&lt;br /&gt;
&lt;br /&gt;
* Starts with (textual values only)&lt;br /&gt;
  * Field startsWith ?   (query notation)&lt;br /&gt;
  * {startsWith: x}    (JS notation)&lt;br /&gt;
&lt;br /&gt;
* Ends with (textual values only)&lt;br /&gt;
  * Field endsWith ?   (query notation)&lt;br /&gt;
  * {endsWith: x}    (JS notation)&lt;br /&gt;
&lt;br /&gt;
* Contains (textual values only)&lt;br /&gt;
  * Field contains ?   (query notation)&lt;br /&gt;
  * {contains: x}    (JS notation)&lt;br /&gt;
&lt;br /&gt;
= Arrays =&lt;br /&gt;
&lt;br /&gt;
Todo: Using [?] inside a filter for array-of-things comparisons.&lt;br /&gt;
&lt;br /&gt;
= Filter functions =&lt;br /&gt;
&lt;br /&gt;
Most often seen in the permission system. These are functions used within a query filter. Filter functions are generic and can also be custom defined by installable modules if you need another. Variations of UserPermits (&amp;quot;CompanyPermits&amp;quot; etc) are quite common.&lt;br /&gt;
&lt;br /&gt;
* IsSelf()&lt;br /&gt;
* HasUserPermit()&lt;br /&gt;
* HasRolePermits()&lt;br /&gt;
* IsIncluded()&lt;br /&gt;
* On(...)&lt;br /&gt;
&lt;br /&gt;
Todo: describing the above, particularly On and IsIncluded&lt;/div&gt;</summary>
		<author><name>72.201.192.210</name></author>
	</entry>
	<entry>
		<id>https://wiki.socialstack.dev/index.php?title=Includes&amp;diff=256</id>
		<title>Includes</title>
		<link rel="alternate" type="text/html" href="https://wiki.socialstack.dev/index.php?title=Includes&amp;diff=256"/>
		<updated>2022-08-11T17:10:14Z</updated>

		<summary type="html">&lt;p&gt;72.201.192.210: /* Lists */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Includes are a mechanism in SocialStack which allow you to request additional content when obtaining an entity (or a list of entities) 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. Includes are not case sensitive.&lt;br /&gt;
&lt;br /&gt;
=Frontend Usage=&lt;br /&gt;
&lt;br /&gt;
Typically includes are used from either Loop or Content:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;dart&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
    import Loop from &amp;#039;UI/Loop&amp;#039;;&lt;br /&gt;
    &lt;br /&gt;
    ...&lt;br /&gt;
    &lt;br /&gt;
    &amp;lt;Loop over=&amp;#039;page/list&amp;#039; includes={[&amp;#039;creatorUser&amp;#039;]}&amp;gt;&lt;br /&gt;
        {page =&amp;gt; {&lt;br /&gt;
            return &amp;lt;div&amp;gt;&lt;br /&gt;
                The page &amp;quot;{page.title}&amp;quot; was created by {page.creatorUser.username}&lt;br /&gt;
            &amp;lt;/div&amp;gt;;&lt;br /&gt;
        }}&lt;br /&gt;
    &amp;lt;/Loop&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;dart&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
    import Content from &amp;#039;UI/Content&amp;#039;;&lt;br /&gt;
    &lt;br /&gt;
    ...&lt;br /&gt;
    &lt;br /&gt;
    &amp;lt;Content type=&amp;#039;page&amp;#039; id={1} includes={[&amp;#039;creatorUser&amp;#039;]}&amp;gt;&lt;br /&gt;
        {page =&amp;gt; {&lt;br /&gt;
            if(!page){&lt;br /&gt;
                // Just an example. The page might also not exist. See &amp;lt;Content&amp;gt; usage for more info.&lt;br /&gt;
                return &amp;#039;Loading..&amp;#039;;&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
            return &amp;lt;div&amp;gt;&lt;br /&gt;
                The page &amp;quot;{page.title}&amp;quot; was created by {page.creatorUser.username}&lt;br /&gt;
            &amp;lt;/div&amp;gt;;&lt;br /&gt;
        }}&lt;br /&gt;
    &amp;lt;/Content&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you aren&amp;#039;t in the React render scope, you can specify includes via a webRequest as well:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;js&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
    webRequest(&amp;#039;page/1&amp;#039;, null, {includes: [&amp;#039;creatorUser&amp;#039;]});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=API Usage=&lt;br /&gt;
&lt;br /&gt;
The frontend is actually just adding any includes on as a GET parameter, like so. If you have more than one, they are comma separated:&lt;br /&gt;
&lt;br /&gt;
https://www.example.com/v1/page/1?includes=creatorUser&lt;br /&gt;
&lt;br /&gt;
=Nested Includes=&lt;br /&gt;
&lt;br /&gt;
You will often have situations where you want to include things on the included objects as well. This is done with a dot:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;js&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
    [&amp;#039;creatorUser&amp;#039;, &amp;#039;creatorUser.tags&amp;#039;]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nesting has no depth limit - you can use 3+ layers deep if you need to. Because you&amp;#039;re spelling out each depth, there is no risk of recursion or cyclical behaviour either - you can include the creatorUser of a creatorUser, even if they are the same object. (And yes, Users do have a creatorUser as well - it can be used to identify if a user account was created by an admin for example).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Wildcards=&lt;br /&gt;
&lt;br /&gt;
You can wildcard include to show all available current level includes, and this is available nested as well:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;js&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
    [&amp;#039;*&amp;#039;, &amp;#039;creatorUser.*&amp;#039;]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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).&lt;br /&gt;
&lt;br /&gt;
Wildcards also do not show explicit includes (ones where you must explicitly list the field name).&lt;br /&gt;
&lt;br /&gt;
=Making something includable=&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
==Single objects==&lt;br /&gt;
&lt;br /&gt;
To make a single object, you&amp;#039;ll need to create a &amp;quot;virtual field&amp;quot; on your C# type:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
    [HasVirtualField(&amp;quot;OtherTypeName&amp;quot;, typeof(OtherTypeName), &amp;quot;OtherTypeNameId&amp;quot;)]&lt;br /&gt;
    public partial class MyCustomType : VersionedContent&amp;lt;uint&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        public uint OtherTypeNameId;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can declare as many virtual fields as you need, but essentially you define:&lt;br /&gt;
&lt;br /&gt;
* The virtual fields name (this is the text which goes inside the includes array)&lt;br /&gt;
* The type of thing that will be included. This must always be a Content type.&lt;br /&gt;
* The name of the field where the ID for your included thing will come from.&lt;br /&gt;
&lt;br /&gt;
For example, this is how creatorUser is actually defined:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
[HasVirtualField(&amp;quot;CreatorUser&amp;quot;, typeof(User), &amp;quot;UserId&amp;quot;)]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Create an includable field called CreatorUser, which will be a User type, and the ID of the user to include comes from the UserId field&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
==Lists==&lt;br /&gt;
&lt;br /&gt;
To include a list of things on a type (for example, a list of answers on a poll), you&amp;#039;ll need to mark the C# type as listable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
    [ListAs(&amp;quot;MyCustomTypes&amp;quot;)]&lt;br /&gt;
    [HasVirtualField(&amp;quot;OtherTypeName&amp;quot;, typeof(OtherTypeName), &amp;quot;OtherTypeNameId&amp;quot;)]&lt;br /&gt;
    public partial class MyCustomType : VersionedContent&amp;lt;uint&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        public uint OtherTypeNameId;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
===Explicit Lists===&lt;br /&gt;
&lt;br /&gt;
There is a special consideration for listing though: by default, your ListAs field will be available to get included on anything. This is great for generic things like tags or categories, but the chances are you probably have something specific in mind. You can just ignore it of course, but the field will list on your admin panel and cause some unwanted clutter.&lt;br /&gt;
&lt;br /&gt;
So, to clamp down where your ListAs field actually appears, use the explicit and implicit modes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
    [ListAs(&amp;quot;MyCustomTypes&amp;quot;, Explicit=true)]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Explicit means the field name must be explicitly used - it won&amp;#039;t appear when the wildcard is used. You can, for example, do this for your includes array:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;js&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
[&amp;#039;*&amp;#039;, &amp;#039;MyCustomTypes&amp;#039;]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The wildcard is what the admin panel uses, so an explicit include will no longer appear there. So, we now have an includable field that exists but appears nowhere by default. We can then make it appear on specific types with ImplicitFor:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
    [ListAs(&amp;quot;MyCustomTypes&amp;quot;, Explicit=true)]&lt;br /&gt;
    [ImplicitFor(&amp;quot;MyCustomTypes&amp;quot;, typeof(ParentTypeToListOn))]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can list as many ImplicitFor attributes as you need. The above means the following:&lt;br /&gt;
&lt;br /&gt;
* This type will be includable with the name &amp;#039;MyCustomTypes&amp;#039;&lt;br /&gt;
* This &amp;#039;MyCustomTypes&amp;#039; include will not appear on wildcard inclusions&lt;br /&gt;
* But it will appear on wildcard inclusions specifically for the type called &amp;#039;ParentTypeToListOn&amp;#039;&lt;br /&gt;
&lt;br /&gt;
=On: Includes without the entity=&lt;br /&gt;
&lt;br /&gt;
There are situations where you&amp;#039;d like a list of includable things but without the thing they&amp;#039;re included on. For example, if you want a paginated list of members. Maybe it&amp;#039;s also live. Ideally we&amp;#039;d want to make use of Loop for this as it has those features, and you can do so using the On filter:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;dart&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;Loop over=&amp;#039;user&amp;#039; filter={{On: {type: &amp;#039;MyCustomType&amp;#039;, id: 1, map: &amp;#039;UserPermits&amp;#039;}}}&amp;gt;&lt;br /&gt;
        ...&lt;br /&gt;
    &amp;lt;/Loop&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above loop will have the same results as having included UserPermits on MyCustomType with ID #1. This allows full access to Loop features on these specialised lists of things.&lt;br /&gt;
&lt;br /&gt;
=Permissions=&lt;br /&gt;
&lt;br /&gt;
Includes are a powerful way to get simple secondary and tertiary permissions - those are permissions where being able to see something relies on being able to see something else. For example, lets say you&amp;#039;re permitted to see messages in a chat if you are a member of the chat.&lt;br /&gt;
&lt;br /&gt;
We can structure this such that the user has a userPermit in the chat, with chat list/load access restricted by the custom rule &amp;lt;code&amp;gt;HasUserPermit()&amp;lt;/code&amp;gt;, and we can restrict message list/load with the custom rule &amp;lt;code&amp;gt;IsIncluded()&amp;lt;/code&amp;gt;. This means a message can only be displayed if you arrived at it via an include, and you can only load the chat (and thus include its messages) if you have a permit. Because includes can be deeply nested, so too can these permissions. The above section about &amp;quot;On&amp;quot; is particularly useful if you want, for example, a live loop of messages which are include restricted.&lt;br /&gt;
&lt;br /&gt;
One important aspect of this is being able to create a new message with the connection to the parent chat immediately. To allow for this, &amp;quot;On&amp;quot; is supported during create calls as well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;js&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
    import webRequest from &amp;#039;UI/Functions/WebRequest&amp;#039;;&lt;br /&gt;
    &lt;br /&gt;
    ...&lt;br /&gt;
    &lt;br /&gt;
    // also from &amp;lt;Form&amp;gt;&amp;#039;s onValues&lt;br /&gt;
    webRequest(&lt;br /&gt;
        &amp;quot;chatmessage&amp;quot;,&lt;br /&gt;
        {&lt;br /&gt;
            text: &amp;#039;Hello! This is a chat message&amp;#039;,&lt;br /&gt;
            on: {&lt;br /&gt;
                type: &amp;#039;Chat&amp;#039;,&lt;br /&gt;
                id: 1,&lt;br /&gt;
                map: &amp;#039;Messages&amp;#039; // If ChatMessage only has one ListAs, i.e. ListAs(&amp;quot;Messages&amp;quot;), declaring map here is optional&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>72.201.192.210</name></author>
	</entry>
	<entry>
		<id>https://wiki.socialstack.dev/index.php?title=Automations&amp;diff=224</id>
		<title>Automations</title>
		<link rel="alternate" type="text/html" href="https://wiki.socialstack.dev/index.php?title=Automations&amp;diff=224"/>
		<updated>2022-08-02T18:43:06Z</updated>

		<summary type="html">&lt;p&gt;72.201.192.210: /* Usage tips */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Automations are a mechanism in SocialStack to create a task which runs on some defined schedule. They&amp;#039;re also known as &amp;quot;background jobs&amp;quot; or &amp;quot;daemons&amp;quot;. The schedule is defined using a [https://en.wikipedia.org/wiki/Cron cron expression].&lt;br /&gt;
&lt;br /&gt;
= Usage =&lt;br /&gt;
&lt;br /&gt;
First, install the Automations module:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
socialstack install Api/Automations&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That adds the Events.Automation method to your project, which can then be used like so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Api.Automations; // for AutomationRunInfo &lt;br /&gt;
&lt;br /&gt;
..&lt;br /&gt;
&lt;br /&gt;
// If your automation doesn&amp;#039;t need to do anything async:&lt;br /&gt;
Events.Automation(&amp;quot;once_a_second&amp;quot;, &amp;quot;* * * * * ? *&amp;quot;)&lt;br /&gt;
	.AddEventListener((Context context, AutomationRunInfo info) =&amp;gt; {&lt;br /&gt;
&lt;br /&gt;
		System.Console.WriteLine(&amp;quot;- Runs once a second -&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		return new ValueTask&amp;lt;AutomationRunInfo&amp;gt;(info);&lt;br /&gt;
&lt;br /&gt;
	});&lt;br /&gt;
&lt;br /&gt;
// With async:&lt;br /&gt;
Events.Automation(&amp;quot;once_a_second&amp;quot;, &amp;quot;* * * * * ? *&amp;quot;)&lt;br /&gt;
	.AddEventListener(async (Context context, AutomationRunInfo info) =&amp;gt; {&lt;br /&gt;
&lt;br /&gt;
		System.Console.WriteLine(&amp;quot;- Runs once a second -&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		await _someOtherService.DoSomething(context);&lt;br /&gt;
		&lt;br /&gt;
		return info;&lt;br /&gt;
&lt;br /&gt;
	});&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that Automation has two args - a name and a schedule. The name is such that your automation can be triggered by hand separately, for example if you need a background job to run &amp;quot;now&amp;quot; rather than waiting for the schedule to trigger it.&lt;br /&gt;
&lt;br /&gt;
Because of the name, it is also possible to add additional handlers to a particular job run. For example if you know a job is called &amp;quot;update_certificate&amp;quot; then you can add an additional handler to the event like so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Events.Automation(&amp;quot;update_certificate&amp;quot;)&lt;br /&gt;
	.AddEventListener((Context context, AutomationRunInfo info) =&amp;gt; {&lt;br /&gt;
&lt;br /&gt;
		System.Console.WriteLine(&amp;quot;- This runs immediately after the update_certificate automation. -&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		return new ValueTask&amp;lt;AutomationRunInfo&amp;gt;(info);&lt;br /&gt;
&lt;br /&gt;
	}, 11); // Priority 11 ensures we for certain will happen after the main automations listener(s) occur.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Usage tips =&lt;br /&gt;
&lt;br /&gt;
* Online cron expression editors can help you [https://www.google.com/search?q=cron+expression+editor get the expression you need]. NOTE: when selecting a cron formater, use one that supports 7 fields - 6 field cron will result in an incomplete expression&lt;br /&gt;
&lt;br /&gt;
* The time is always in UTC, meaning it is not affected by local timezone changes such as daylight savings and it is easier to sync larger jobs within a global cluster.&lt;/div&gt;</summary>
		<author><name>72.201.192.210</name></author>
	</entry>
	<entry>
		<id>https://wiki.socialstack.dev/index.php?title=Automations&amp;diff=223</id>
		<title>Automations</title>
		<link rel="alternate" type="text/html" href="https://wiki.socialstack.dev/index.php?title=Automations&amp;diff=223"/>
		<updated>2022-08-02T18:05:37Z</updated>

		<summary type="html">&lt;p&gt;72.201.192.210: /* Usage tips */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Automations are a mechanism in SocialStack to create a task which runs on some defined schedule. They&amp;#039;re also known as &amp;quot;background jobs&amp;quot; or &amp;quot;daemons&amp;quot;. The schedule is defined using a [https://en.wikipedia.org/wiki/Cron cron expression].&lt;br /&gt;
&lt;br /&gt;
= Usage =&lt;br /&gt;
&lt;br /&gt;
First, install the Automations module:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
socialstack install Api/Automations&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That adds the Events.Automation method to your project, which can then be used like so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Api.Automations; // for AutomationRunInfo &lt;br /&gt;
&lt;br /&gt;
..&lt;br /&gt;
&lt;br /&gt;
// If your automation doesn&amp;#039;t need to do anything async:&lt;br /&gt;
Events.Automation(&amp;quot;once_a_second&amp;quot;, &amp;quot;* * * * * ? *&amp;quot;)&lt;br /&gt;
	.AddEventListener((Context context, AutomationRunInfo info) =&amp;gt; {&lt;br /&gt;
&lt;br /&gt;
		System.Console.WriteLine(&amp;quot;- Runs once a second -&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		return new ValueTask&amp;lt;AutomationRunInfo&amp;gt;(info);&lt;br /&gt;
&lt;br /&gt;
	});&lt;br /&gt;
&lt;br /&gt;
// With async:&lt;br /&gt;
Events.Automation(&amp;quot;once_a_second&amp;quot;, &amp;quot;* * * * * ? *&amp;quot;)&lt;br /&gt;
	.AddEventListener(async (Context context, AutomationRunInfo info) =&amp;gt; {&lt;br /&gt;
&lt;br /&gt;
		System.Console.WriteLine(&amp;quot;- Runs once a second -&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		await _someOtherService.DoSomething(context);&lt;br /&gt;
		&lt;br /&gt;
		return info;&lt;br /&gt;
&lt;br /&gt;
	});&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that Automation has two args - a name and a schedule. The name is such that your automation can be triggered by hand separately, for example if you need a background job to run &amp;quot;now&amp;quot; rather than waiting for the schedule to trigger it.&lt;br /&gt;
&lt;br /&gt;
Because of the name, it is also possible to add additional handlers to a particular job run. For example if you know a job is called &amp;quot;update_certificate&amp;quot; then you can add an additional handler to the event like so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Events.Automation(&amp;quot;update_certificate&amp;quot;)&lt;br /&gt;
	.AddEventListener((Context context, AutomationRunInfo info) =&amp;gt; {&lt;br /&gt;
&lt;br /&gt;
		System.Console.WriteLine(&amp;quot;- This runs immediately after the update_certificate automation. -&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		return new ValueTask&amp;lt;AutomationRunInfo&amp;gt;(info);&lt;br /&gt;
&lt;br /&gt;
	}, 11); // Priority 11 ensures we for certain will happen after the main automations listener(s) occur.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Usage tips =&lt;br /&gt;
&lt;br /&gt;
* Online cron expression editors can help you [https://www.google.com/search?q=cron+expression+editor get the expression you need]. NOTE: when selecting a cron formater that supports 7 fields - 6 field cron&lt;br /&gt;
&lt;br /&gt;
* The time is always in UTC, meaning it is not affected by local timezone changes such as daylight savings and it is easier to sync larger jobs within a global cluster.&lt;/div&gt;</summary>
		<author><name>72.201.192.210</name></author>
	</entry>
</feed>