Pulsar Settings Language - Overview

Definition

Pulsar Settings Language (PSL) is a domain-specific scripting language for creating custom behavior within the Pulsar environment. This provides Pulsar admins with functionality similar to that provided by validation rules, triggers, and formulas within the Salesforce environment. Your custom PSL is typed directly into the 'value' field for a Pulsar Setting that supports it.

PSL is supported for a subset of Pulsar Settings where a potentially complicated calculation, workflow, or validation is necessary. These are the settings where PSL is used.

  • Field default values

  • Field formulas

  • Object validations and triggers (beforeConvert, beforeEdit, beforeSave, beforeDelete, onSave, onCreate, onDelete)

    • Note about onCreate. It is most useful when you use it with preInit argument. This will give you a chance to pass values from the parent to the child. 

  • Custom buttons

At the core of PSL is an 'Action' that is executed by Pulsar.  There are several types of actions you may choose from, but any action first needs to be included within a 'block'.  

Blocks

A PSL setting is made up of one or more 'blocks', and your PSL must contain at least one block.  A block is a group of sequential actions to perform where each action is separated by the vertical bar (pipe) character, "|". A block may only contain Actions and not other blocks. Generally, each action within the block will be executed in order until the end of the block is reached.  

Block Syntax

Block names must be in all CAPS and must not contain any spaces.  The block name is then followed by the left curly brace "{", and after the actions are specified, is completed with the right curly brach "}".  Here is an example of the standard 'DEFAULT' block containing two actions.
DEFAULT{
<Action 1>;
|
<Action 2>;
} 

Standard Blocks

Standard blocks are used to define the entry point into your PSL. Although other standard blocks exist, we recommend that you use the 'DEFAULT' block as it can be used for both simple and complicated settings.   

Custom Blocks

You may define your own blocks of actions using any block name you would like.  However, please keep in mind that you will still need your 'DEFAULT' block in order to start your setting off. From your DEFAULT block, you can then branch to your custom blocks.

Actions

An action consists of a few statements, the most important of which is the 'Action' statement which defines what type of action to do.

Action Syntax

Each action is made up one or more lines each containing a 'key=value' pair terminated by a semicolon.  The first line should be the 'Action=' text followed by the Action Type (see below for a list of action types):  
Action=<ActionType>;

Subsequent 'key=value' pairs on this action are documented on the Action itself. Below is a reference to the most popular language actions:

Action=<Execute Another Block>

To specify that the flow should go to another block, specify the block name using a prefix of two underscores "__".  Typically, you would have this action as the last action in the block as it will stop executing any remaining actions in the current block. There are no additional keys for this action type.

Example (Execute a block named 'BLOCK_2'):

Action=__BLOCK_2;

Action=Alert

To display an alert to the user. This is useful for communicating validation messages to the user and for debugging the setting itself (by printing out variables). 

Additional Parameters:
Message  = <Required; A text message; You may include newlines, white space, and variables>;

AlertType = <Not Required> 

Values supported:

DismissAlert - The default single-button OK alert.  (Same as not specifying an AlertType parameter).  NOTE: this will by default invalidate the PSL trigger, which you can change via additional action param:

  • AlertShouldValidate = TRUE or (default FALSE). Use this to change the validation behavior.

DismissCurrentWindow - Use this wisely because you would not want to dismiss an edit window but perhaps upon successful execution of a setting, e.g.: onBarcodeScan setting. (Upon successfully executing the setting, you would want to dismiss the underlying camera screen at that point, saving the user an extra click). Additional action param:

  • AlertShouldValidate = FALSE or (default TRUE). Use this to change the validation behavior.

BranchChoice - This alert will present the user with two choices (as buttons). For each choice you should specify both the title and the name of the action block taken. This type of alert should be the last action in a block since no matter which choice is selected, it will not continue executing actions on the current block. Refer to example 2 below.
Here are the additional action parameters expected for this alert type:

  • YesButtonTitle = a string that is displayed on the one of the alert buttons.  Note, the string does not need to be associated with a 'Yes' to a question posed by the alert; it only needs to correspond with the 'YesButtonAction' below.

  • NoButtonTitle = a string that is displayed on the one of the alert buttons.  Note, the string does not need to be associated with a 'No' to a question posed by the alert; it only needs to correspond with the 'NoButtonAction' below.

  • YesButtonAction = the name of the block in your setting that should be executed if the user selected the corresponding choice indicated by the YesButtonTitle parameter.

  • NoButtonAction = the name of the block in your setting that should be executed if the user selected the corresponding choice indicated by the NoButtonTitle parameter.

ChoiceAlert - This will present the user two choices (as Yes/No buttons). For each choice you should specify the title of the buttons if they will be different than 'Yes'/'No'.  If the user selects the Yes button, actions will continue to execute on the block, otherwise, execution on the current block will stop.  Refer to example 3 below.  This example block would be executing inside a 'beforeSave' setting.  Here we prompt the user to confirm they want to save the record. Clicking on the Yes button will save the record. Clicking on the No button will continue to display the edit window giving the user a chance to change the values. 
Addition parameters you could use if you are using the ChoiceAlert: 

  • YesButtonTitle = a string relevant to your environment to replace the default string 'Yes' on the choice alert.

  • NoButtonTitle = a string relevant to your environment to replace the default string 'No' on the choice alert.

Example:

DEFAULT{ Action=Alert; Message=This is how the Alert works. Pretty neat! Huh?; }

Example 2:

DEFAULT { Action=Alert; Message=What is 2+2?; AlertType=BranchChoice; YesButtonTitle=4; NoButtonTitle=6; YesButtonAction=CORRECT; NoButtonAction=INCORRECT; } CORRECT { Action=Alert; Message=Well done! 2+2 does = 4; } BLOCK_TWO { Action=Alert; Message=Erm.... You might want to try again; }

Example 3:

Action=CloneCurrentObject

To show an object in create mode pre-populated with values from the current object. Please note that this action is not intended to be used on an object that's currently in edit or create mode. A good location for this action would be a custom button.

Additional Keys:
ExcludeFields = <Optional; A comma separated list of field API names to not copy to new object>;

Example:

Action=CloneRecentObject

To show an object in create mode pre-populated with values from the most recent object of the current object's type. The SortClause key is used to calculate whether an object is recent. This action is intended to be used with the 'onCreate' setting meaning that it overrides the normal 'Add' button action from a related list context.

Additional Keys:
ObjectType = <Required; The object API name>;
SortClause = <Required; The SQL sort clause (without the ORDER BY text)>;
MatchFields = <Optional; A comma separated list of field API names to filter this 'recent' query>;
ExcludeFields = <Optional; A comma separated list of field API names to not copy to new object>;

Example (clone the Account's most recent Opportunity when creating a new Opportunity from that Account):

Action=Create

To show an object in create mode with no special pre-population of fields. This is equivalent to the user pressing the Add button from a top level list.

Additional Keys:
ObjectType = <Required; The object API name>;

Example:

Action=CreateAndMapFields

To create an object and optionally specify a field mapping from the current object to the new object. Note, you may also specify literals or variables as the source of the field mapping (right hand side) instead of a source field. The field mapping is then used to pre-populate the field values. This method is recommended over the 'Create' action as it has more flexibility. Note, when using this method to create an object with multiple record types, we recommend that you also specify either the 'RecordTypeId=' key and specify the Id or the 'RecordTypeName=' key and specify the record type's developer name. Otherwise, Pulsar will prompt the user to select a record type before the object is shown. Setting the ActionShouldComplete key to 'TRUE' means that the object will be automatically saved if possible.  The ActionShouldDisplay setting refers to the automatic display of an object after its creation; If your PSL is creating multiple objects, you should set this key to FALSE.

Additional Keys:
ObjectType = <Required; The object API name>;
ActionShouldComplete = <Optional; Object should automatically and immediately save; default is FALSE>;
ActionShouldDisplay = <Optional; Object should automatically display upon successfully creating an object; default is TRUE>;
RecordTypeName = <Optional; This will specify that the new record is of RecordTypeId for the given name value>;
<New Object Field API Name 1> = <Optional; This can be literal value surrounded by double quotes or a single field expression based on the current object>;
<New Object Field API Name 2> = <...>;
... 

Example (Auto save and display the created object; 4 fields were mapped to a literal, a source field, and two variables):

Action=SFCreate

To create an object and push it directly to Salesforce, bypassing the local database entirely. The syntax is the same as in CreateAndMapFields, with the exception of ActionShouldComplete and ActionShouldDisplay, which are not supported. 

Additional Keys:
ObjectType = <Required; The object API name>;
<New Object Field API Name 1> = <Optional; This can be literal value surrounded by double quotes or a single field expression based on the current object>;
<New Object Field API Name 2> = <...>;
... 

Example (create an Event object on the server; 4 fields mapped to a literal, a source field, and two variables):

Action=SFUpdate

To update an object and push the update directly to Salesforce, bypassing the local database entirely. The syntax is the same as in CreateAndMapFields, with the exception of ActionShouldComplete and ActionShouldDisplay, which are not supported. The object's Id is also required.

Additional Keys:
ObjectType = <Required; The object API name>;
Id = <Required; The object Id>;
<New Object Field API Name 1> = <Optional; This can be literal value surrounded by double quotes or a single field expression based on the current object>;
<New Object Field API Name 2> = <...>;
... 

Example (create an Event object on the server; 4 fields mapped to a literal, a source field, and two variables):

Action=Delete

To delete an object with the specified object Id

Additional Keys:
ObjectType = <Required; The object API name>;
ObjectId = <Required; The object Id to delete.>;

Example:

Action=Display

To show an object in display mode.

Additional Keys:
ObjectType = <Required; The object API name>;
ObjectId = <The object Id to display. If not specified, then you should specify the 'ObjectIdField' key>;
ObjectIdField = <The field API name for the reference field containing the Id of the object you want to display>;

You must specify either ObjectId or ObjectIdField parameters.  If both are specified, ObjectId takes precedence.

Example:

Action=GetCustomLabels

The GetCustomLabels Action allows you to retrieve the Salesforce Custom Labels that have been previously post-processed into auto-generated Pulsar Settings.  This Action will return the Labels specified in the Locale specified as a list of PSL variable names.

Additional Keys:
Locale  = lang_Locale of the labels in question;

Labels = list of the API names for the labels you would like to retrieve, separated by commas

ReturnLabels = List of the variable names in order of the Labels specified... (Make sure to ensure that the number of variable names match the list of labels)

Example:

Action=GetObjectType

The GetObjectType Action takes an ObjectId parameter, and retrieves and stores the object API name into a variable named by the VarName parameter.

Additional Keys:

ObjectId = an objects 15 or 18 character ID (or a variable with such a value)

VarName = name of variable to store object API name (type)

Action=IsChanged

When writing Validation rules and field update triggers it may be necessary to test whether a particular field or numeric field formula changed from it's stored value during editing of the record. The IsChanged Action provides a way to do this and store the result in a variable. The resulting value stored in the variable will be "TRUE" if changed, or "FALSE" otherwise. You can specify a simple numeric formula (+,-,*,/) as long as all your fields in the formula are a numeric type. 

Additional Keys:
FieldName= <Required; The fieldname or formula to check>;
ReturnValue= <Required; Variable to store the "TRUE" or "FALSE" result>;

Example:

Action=IsNew

It's often useful to know whether the current object that PSL is acting upon is a newly created SObject or not. The IsNew Action provides a way to do this and store the result in a variable. The resulting value stored in the variable will be "TRUE" if the object is new, or "FALSE" otherwise. 

Additional Keys:
ReturnValue= <Required; Variable to store the "TRUE" or "FALSE" result>;

Example:

Action=LaunchDocument

Launches an HTML document that opens after specific execution points

  • Available for: onSave, onDelete, onCreate, and buttonActions ("custom button")

  • Can be used with a custom button

Variables are passed to the document as URL parameters. The ObjectID and ObjectType of the current page are included as well as any default values passed in the execution point. Additional variables can be passed using the SetVar PSL action described above. SetVar must be before the LaunchDocument Action in the PSL block.

For onSave and onDelete, the HTML document is launched after these actions occur. The onCreate execution point is used for the creation of child objects. This action is different: the HTML document will replace the existing UI pathway for the creation of that object.

Additional Keys:
DocumentId  = <DocumentID e.g. 069i0000001i3wP>;

Example:

Example with SetVar:

Action=Log

To write a message to logs. This is useful for debugging settings (by printing out variables). PSL Action logs are printed at the INFO level and have the format: [PSL] Log Action: <your message>

Additional Keys:
Message  = <A text message; You may also include variables in the message>;

Example:

Action=Loop

To repeatedly execute a block of actions.  The variable specified by the CountVar parameter always starts at 1 and will increment by 1 at the start of each subsequent loop iteration.  The maximum of times the loop will run is specified by the CountTo parameter.

Additional Keys:
BlockName = <Required; The name of the block containing the actions to execute a specific number of times>;
CountVar = <Required; The name of the counter variable>;
CountTo = <Required; The total number of times the loop should run>;

Simple Loop Example:

Complex Loop Example (nested loops with early exiting):

Action=OpenURL

To launch and send information to another app. This action relies on the target App to have already implemented at least one custom URL scheme, or support Universal Links.  Please consult with that App's author for details on how to construct a URL that their app will recognize and process correctly. Currently, we have whitelisted these schemes (yourekamobile, skype, augment, sharinpix, prontoforms).  Please contact us to suggest additional schemes to whitelist.

Additional Keys:
URL = <Required; The custom URL that the 3rd party app supports;>

OpenInExternalBrowser = <Optional, TRUE or default FALSE>. If TRUE, will open the URL in an external app/browser. If FALSE (default) will open the url within the Pulsar app.

Example (Start the Skype app and dial a phone number):

Action=QuickAction

The QuickAction Action allows you to run a Salesforce Quick Action in a specific object context. The ContextId parameter can be omitted if for global Quick Actions.

Additional Keys:
Name  = <Required; This is the unique API name of the Quick Action>;

ContextId = <Optional; The Id of the parent SObject for the QuickAction. For example, an Account may have a NewContact Quick Action that takes the Account Id as the ContextId>;

Example:

Action=ReadBluetooth

This action allows reading a value from a bluetooth device.

Additional Keys:
Type= <Required; the type of value being returned. Valid settings are string or number.>;
Device= <Required; The friendly name for the device. If not found in the list of broadcasting bluetooth devices within range, a prompt of all available devices will be shown.>;
Service= <Required; The UUID of the service you want to target on the bluetooth device.>;
Characteristic= <Required; The UUID of the characteristic you want to target on the bluetooth device.>;
VarName= <Required; The PSL var that will receive the result of the bluetooth read.>;

The below example assumes you have a bluetooth device (or you use something like LightBlue to mock a virtual bluetooth device)

  • Device called Test Bluetooth Peripheral

  • Service with UUID = 1111

  • Characteristic with

    • UUID = 2222

    • Description = Test Characteristic

    • Hex = ---whatever test value you want---

Example:

Action=RegisterNotification

This action sets a local reminder for the user.  For example, after updating the status of a record, the user can be reminded after an hour to perform another related task.

Additional Keys:
Message= <Required; The text displayed in the notification to the user. Variables are supported here.>;
AfterTimeDelay= <Required; The time (in minutes) from the current time for when to notify the user.>;

Example:

Action=SetField

See action 'SetFieldInMemory' for more information. NOTE: This action differs from SetFieldInMemory in that it will automatically save and refresh the displayed object after updating the field. Due to this, it is only recommended to use this action in custom buttons, and not for object triggers and validation rules, to avoid unintended side effects.

Action=SetFieldInMemory

To update a single field in the current object while in edit or create mode. Typically you would use 'General' as the FieldType as this will cause the field to be updated with the value in FieldValue.  Please note that after resolving any variables (e.g., %%X%%) contained in FieldValue, the resulting value is interpreted as a literal and used to update the field.  Specifying a FieldType of 'Timestamp' will ignore the FieldValue key and use the current time instead.

Additional Keys:
FieldType = <Optional; The type of field update; Valid values include 'General', 'Timestamp', and 'Image'>;  If not specified, type is assumed to be 'General'.
FieldName = <Required; The field API name>;
FieldValue = <Required if 'General' FieldType; After resolving any variables, this field will be updated with the literal value.>; 
ImageType = <Required if 'Image' FieldType; Valid values include 'ImageResource'> 

Example (Update the Description field with the variable named Description):

Action=SetLocation

See action 'SetLocationInMemory' for more information. This action differs from SetLocationInMemory in that it will automatically save the object after updating the field. Due to this, it is only recommended to use this action while in display mode. A custom button is a good place for this action.

Action=SetLocationInMemory

To update a Salesforce Geolocation field in the current object while in edit or create mode. The LocationType of 'DeviceLocation' will record the device's current GPS location (latitude/longitude) if the user has granted permission for Pulsar to obtain this information. Initially fetching the location may be a slow process, so you may consider adjusting the pulsar.location.updateFrequencySeconds Pulsar Setting to tune the tradeoff between accuracy and speed.

Additional Keys:
FieldName = <Required; The field API name of your geolocation field>;
LocationType = <Required; The type of location; Valid values include 'DeviceLocation'>;
LocationAccuracy = <Optional; Controls the accuracy of the geolocation data collected when the app is running in the foreground; Valid values include 'Fine (10 meters), Medium (100 meters), Coarse(1000 meters)'; Medium is the default>;

Example:

Action=SetResult

This action allows you to set a result string and a TRUE or FALSE value. This is often used in the context of custom buttons, or to fail validation with an error message.

Additional Keys:
Result = <Required; a string value>
ResultValid = <Optional; a TRUE or FALSE value>

Action=SetVar

This action allows you to store a value into a variable. In order to retrieve the value from the variable, you would enclose the variable name in double percent signs (%%My_Var_Name%%). Once stored, the variable can then be accessed across all your blocks within your setting. The VarName can be any variable name you like as long as it doesn't contain any spaces or any special characters besides the underscore character. The VarValue can be a literal, a field, a special Pulsar value, a numeric formula, or another variable. You specify a literal value by surrounding the value with double quotation marks (for values of all field types). You specify a field using SOQL Relationship syntax from the current object to obtain the value of a field in the current or a related object. You can specify a simple numeric formula (+,-,*,/) as long as all your fields in the formula are a numeric type. Lastly, the VarValue can also be set to special Pulsar values (for example: '@@CurrentUserId' for the current Pulsar user's Id; '@@Today' for the current date;  Full Listing).

Additional Keys:
VarName = <Required; A variable name>;  NOTE: variable names are case sensitive but the best practice is to NOT rely on this (in other words having both somevariable1 and SomeVariable1 is NOT RECOMMENDED)
VarValue = <Required; An expression, see action description above and examples below>;

Example (literal value):


Example (special Pulsar value for user id):


Example (field in current object):


Example (field in related object: Account Name from Opportunity object):


Example (numeric formula):

 

Example appending/interpolation:

 

Example embedding/interpolation with quoting:

 

Example special characters

 

Example case sensitivity

 

Example (Geo Location): Notice how you can access the long and lat values by simply adding __Latitude__s and __Longitude__s to the variable you have used on the top.


Example (Geo Location) complicated SFDC formula usage

 

Example (variable retrieval and usage):


Example (view variable value for messaging or debugging purposes):

 

A Special Example of SetVar (Convert SQLite Time to SFDC Time): We need this because the date time format from SQLite is different from SFDC. This will help you set timestamps the way SFDC expects them.

Full List of Special Values

@@CurrentUserId = The current Pulsar user's Id
@@CurrentUsername = The current Pulsar user's username
@@CurrentUserLocale = The current Pulsar user's locale Id
@@CurrentUserFullName = The current Pulsar user's full name
@@CurrentProfileId = The current Pulsar user's profile Id
@@CurrentProfileName = The current Pulsar user's profile name (Access to this value requires the user to have access to the "Profile" SObject)
@@CurrentRoleId = The current Pulsar user's role Id
@@CurrentRoleName = The current Pulsar user's role Name (Access to this value requires the user to have access to the "UserRole" SObject)
@@CurrentOrganizationId = The Org Id of the current Pulsar user
@@Today = Current date
@@Now = Current date-time formatted for the Salesforce API, which can be used in to fill Salesforce datetime fields
@@ConvertToSFTime = converts a sqlite timestamp to a SFDC timestamp. Requires using the TimeString parameter of the SetVar Action. See example above.
@@CurrentScanCode = if you are using the barcode scanner to access the value that was just read from a barcode or a QR code
@@CurrentLocation = grabs the latitude and longitude coordinates (with medium location accuracy) of the device's current position. These values are then accessible by using the resulting variable name and appending '__Latitude__s' and '__Longitude__s' to the variable name.  If you require fine- or coarse-grained acuracy, consider using the SetLocation/InMemory action/s. NOTE: it may be a slow to retrieve the current location, but judicious use of pulsar.location.updateFrequencySeconds setting may help speed this up.
@@CreatedObjectId = This is used during the onSave trigger for retrieving the id for the object that was just created.
@@AppVersion = The current Pulsar App's version, e.g. "4.1.0.101"
@@LastSyncSuccess = This is TRUE or FALSE depending on the success or failure of the last sync attempt
@@LastSyncTime = The date/time of the last successfully completed sync. "1970-01-01T00:00:00.000Z" will be returned when a sync has never completed.
@@LastSyncDuration = The last sync duration in seconds. 0 will be returned when a sync has never completed.

@@LastFailedSyncTime = The date/time of the last failed sync. "1970-01-01T00:00:00.000Z" will be returned if a sync has never failed.
@@LastFailedSyncDuration = The last failed sync duration in seconds. 0 will be returned if a sync has never failed.

// See the After Sync Trigger page for more sync stats. 

Action=SqlQuery

Caveat Programmer!

SqlQuery is the most powerful Action as it allows you to directly access Pulsar's underlying Sqlite database, and extreme care should be taken with its use. Although you may run data manipulation queries, we recommend that you limit your access here to 'SELECT' or read type queries.

If you run data manipulation (UPDATE) queries, please note:

  • Pulsar will not process ValidationRules for the record(s) in question

  • Pulsar will not re-calculate and save formula fields for the record(s) in question

  • Pulsar will not re-calculate roll-up summary fields on parent object(s) records(s) for record(s) in question

  • It is possible to write values to the Db offline that will not sync cleanly to Salesforce or may damage your Salesforce instance when synced

  • It is possible to write values to the Db that will break functionality across the Pulsar app

The SqlQuery action is typically used when you need to perform more complicated actions, formatting, or conditional branching to other blocks. The QueryString is where you specify the query, and you may use variables and the full range of Sqlite functions here. For proper SQL syntax and a list of available SQL functions, please refer to http://sqlite.org.

To conditionally branch to another block, you must specify a QueryTest expression. Also, you must specify a block name (no underscore prefix is necessary) to either the QueryTestTrue or QueryTestFalse key to branch the flow to that block. If the QueryTest result is TRUE and the QueryTestTrue key is missing or the QueryTest is FALSE and the QueryTestFalse key is missing, the flow will simply continue to the next action in the current block. 

Additional Keys:
QueryString = <Required; The SQL query>;
QueryReturnFields = <Optional, but required for SELECT queries; A comma separated list of SQL column names from the QueryString that you want stored into variables>;
QueryTest = <Optional; A SQL formula comparison in which the result is TRUE or FALSE>;
QueryTestTrue = <Optional; The block name to branch to if the result was TRUE>;
QueryTestFalse =   <Optional; The block name to branch to if the result was FALSE>;
QueryLoop =   <Optional; The block name that will run for each row returned from a SELECT query.>; 

 

Example (store the Product name and price from a custom object into variables, assuming X was defined earlier):


Example (SQL 'CASE' function):


Example (Datetime functions):


Example (String concatenation: Use the printf function instead of the '||' concatenation operator):


Example (A complete solution utilizing the SqlQuery action along with other action types to create an object only when required):

 
QueryLoop Example.  Here we use QueryLoop to iterate through child records and build a string:

 

Action=SyncNow

The SyncNow Action allows you to start a data sync. This behaves similarly to pressing Sync Now from the Pulsar home screen. 

NOTE: Please keep in mind that Pulsar data sync is asynchronous. Using the SyncNow action will start the data sync process in the background, allowing the user to continue using the app.  The executing PSL will also continue after this action is called. It is NOT recommended to use this action within an onsave and other sobject triggers as there is potential for conflicts between the running sync and the save process in progress.

Additional Keys:

SyncType = <Optional; The type of sync; At this time, the only valid value is 'PUSH'>;  If not specified, a standard sync will run.
UseComposite = <Optional; Specify that a Push sync should use Composite API. Valid values are TRUE or FALSE>;
UseCompositeGraph = <Optional; Specify that a Push sync should use Composite Graph API. Valid values are TRUE or FALSE>;

If the Composite API or the Composite Graph API setting is specified, that value is used as the default behavior for all sync types. However, if the UseComposite or UseCompositeGraph option is specified, then that value takes precedence over the setting.

Example with standard sync:

Example with Composite Graph enabled Push sync: