Is there a way to have guarded transitions within Hierarchical State Nodes in xState - state-machine

As title implies, I have a guarded transitions that I wanted to be in a Hierarchical State Node, however it seems that xState can not read property of guards and returns a "TypeError: Cannot read property 'propertyName' of undefined" error"
Is there a way to do this in xState, or should I proceed without hierarchical State Node in this case

Right so issue I was having was that i had context assigned to my hierarchical state machine instead of the parent machine, so guards couldn't access it. Moving context to the parent machine has solved the issue

I used the hierarchical state machine example from the docs to try and model this, adding the following in the tail end of the example:
on: {
POWER_OUTAGE: '.red.blinking',
POWER_RESTORED: '.red',
POWER_TEST: {
target: '.red.stop',
cond: {
type: 'test'
}
}
}
},{
guards: {
test: () => true
}
});
This seems to work as expected, you can try this machine and guard example in the visualizer here
You can flip the boolean in the test guard to play around with it working/not working.
And here is the complete code of the example for reference:
const pedestrianStates = {
initial: 'walk',
states: {
walk: {
on: {
PED_COUNTDOWN: 'wait'
}
},
wait: {
on: {
PED_COUNTDOWN: 'stop'
}
},
stop: {},
blinking: {}
}
};
const lightMachine = Machine({
key: 'light',
initial: 'green',
states: {
green: {
on: {
TIMER: 'yellow'
}
},
yellow: {
on: {
TIMER: 'red'
}
},
red: {
on: {
TIMER: {
target: 'green',
cond: {
type: 'searchValid'
}
}
},
...pedestrianStates
}
},
on: {
POWER_OUTAGE: '.red.blinking',
POWER_RESTORED: '.red',
POWER_TEST: {
target: '.red.stop',
cond: {
type: 'test'
}
}
}
},{
guards: {
test: () => true
}
});

Related

node-ews Update email to mark as read

I'm using "node-ews" library version 3.5.0, but when I try to update any property I get the following error:
{
"ResponseMessages":{
"UpdateItemResponseMessage":{
"attributes":{
"ResponseClass":"Error"
},
"MessageText":"An internal server error occurred. The operation failed., Object reference not set to an instance of an object.",
"ResponseCode":"ErrorInternalServerError",
"DescriptiveLinkKey":0,
"Items":null
}
}
}
I'm trying to mark email as read using the following code:
const markFolderAsRead = async (ews, id, changeKey) => {
const args = {
attributes: {
MessageDisposition: "SaveOnly",
},
ItemChanges: {
ItemChange: {
ItemId: {
attributes: {
Id: id,
ChangeKey: changeKey,
},
},
Updates: {
SetItemField: {
FieldURI: {
attributes: {
FieldURI: "message:IsRead",
},
Message: {
IsRead: true,
},
},
},
},
},
},
};
await ews.run("UpdateItem", args).then((result) => {
console.log("email read:", JSON.stringify(result));
});
};
I tried several modifications, including trying to update another fields, but none of it worked.
I followed this documentation: https://learn.microsoft.com/pt-br/exchange/client-developer/web-service-reference/updateitem-operation
And the lib doesn't show any example of it, but when I change the json to a wrong "soap" construction the error show different messages, or even if I do not pass any of the parameters required as "ChangeKey".
So, maybe this error is something relate to microsoft ews soap construction that I'm missing parameters, or so.
Got it working!
My JSON was wrong. The FieldURI was finishing after the message attribute, it should be before.
Correct JSON:
const args = {
attributes: {
MessageDisposition: "SaveOnly",
ConflictResolution: "AlwaysOverwrite",
SendMeetingInvitationsOrCancellations: "SendToNone",
},
ItemChanges: {
ItemChange: {
ItemId: {
attributes: {
Id: id,
ChangeKey: changeKey,
},
},
Updates: {
SetItemField: {
FieldURI: {
attributes: {
FieldURI: "message:IsRead",
},
},
Message: {
IsRead: "true",
},
},
},
},
},
};

How to fix node-abac module "Cannot set property '_validator' of undefined"

I'm trying to use the node-abac module for Access Control. I realize it hasn't been updated in 4 years (at time of writing), but maybe this is fixable.
After setting up node-abac with a policy, I receive the titular error at runtime here:
node_modules\node-abac\lib\abac.js:27 this._validator = new Validator(Policy);
Policy:
const nodeAbac = require('node-abac');
const accessPolicy = {
attributes: {
user: {
role: 'Is A Site Owner',
dateJoined: 'Member for over 1 year'
}
},
rules: {
'fullAccess': {
attributes: {
'user.role': {
comparison_type: 'String',
comparison: 'isStrictlyEqual',
value: 'owner'
}
}
},
'canModerateReviews': {
attributes: {
'user.dateJoined': {
comparison_type: "datetime",
comparison: "isLessRecentThan",
value: "-1Y"
},
'user.reviews.length': {
comparison_type: 'numeric',
comparison: 'isGreaterThanEqualTo',
value: '12'
}
}
}
}
}
const accessControl = nodeAbac(accessPolicy);
module.exports = accessControl;
Found the issue. I was constructing accessControl incorrectly, without the new keyword.
const accessControl = new nodeAbac(accesspolicy);
Now on to find other implementation errors!

How to filter children in tree structure in Tabulator?

I tried callingsetFilter function on my Tabulator tree structure, in order to filter out items. It seems to only filter out top parents. Any idea how to make this work for any level (any children or parents)? http://tabulator.info/docs/4.1/tree doesn't say much about how filtering works.
Function
table.setFilter('id', '=', 214659) is not returning anything...
Tree structure
[
{
"level":0,
"name":"word1",
"id":125582,
"_children":[
{
"level":1,
"name":"word6",
"id":214659
},
{
"level":1,
"name":"word7",
"id":214633
},
{
"level":1,
"name":"word2",
"id":214263,
"_children":[
{
"level":2,
"name":"word8",
"id":131673
},
{
"level":2,
"name":"word9",
"id":125579
},
{
"level":2,
"name":"word10",
"id":125578
},
{
"level":2,
"name":"word4",
"id":172670,
"_children":[
{
"level":3,
"name":"word13",
"id":172669
},
{
"level":3,
"name":"word14",
"id":174777
},
{
"level":3,
"name":"word5",
"id":207661,
"_children":[
{
"level":4,
"name":"word15",
"id":216529
},
{
"level":4,
"name":"word16",
"id":223884,
"_children":[
{
"level":5,
"name":"word17",
"id":223885,
"_children":[
{
"level":6,
"name":"word18",
"id":229186,
"_children":[
{
"level":7,
"name":"word19",
"id":219062
},
{
"level":7,
"name":"word20",
"id":222243
}
]
}
]
}
]
}
]
}
]
},
{
"level":2,
"name":"word3",
"id":214266,
"_children":[
{
"level":3,
"name":"word11",
"id":216675
},
{
"level":3,
"name":"word12",
"id":216671
}
]
}
]
}
]
}
]
After a little searching found out an extension for lodash library called deepdash which has deep level filtering and it works quite well.
You will have 2 new dependencies but I think it will serve your purpose.
Check the documentation on how to install them here
In the snippet here you can see in the log the results. I made a sandbox also here
This is for a list of ids, one or more.
If you need only for one value change the conditional. return _.indexOf(idList, value.id) !== -1; to return id===value.id; where id is your id variable
Also after looking at the documentation from Tabulator, the have only one level filtering, even if you write your own custom filter it wouldn't help, because it expects a bool value to render the row or not. But only for the first level, so if the parent is not what you look for the child will be ignored. The only option for you is to filter the data outside the Tabulator.
const data = [
{
level: 0,
name: "word1",
id: 125582,
_children: [
{
level: 1,
name: "word6",
id: 214659
},
{
level: 1,
name: "word7",
id: 214633
},
{
level: 1,
name: "word2",
id: 214263,
_children: [
{
level: 2,
name: "word8",
id: 131673
},
{
level: 2,
name: "word9",
id: 125579
},
{
level: 2,
name: "word10",
id: 125578
},
{
level: 2,
name: "word4",
id: 172670,
_children: [
{
level: 3,
name: "word13",
id: 172669
},
{
level: 3,
name: "word14",
id: 174777
},
{
level: 3,
name: "word5",
id: 207661,
_children: [
{
level: 4,
name: "word15",
id: 216529
},
{
level: 4,
name: "word16",
id: 223884,
_children: [
{
level: 5,
name: "word17",
id: 223885,
_children: [
{
level: 6,
name: "word18",
id: 229186,
_children: [
{
level: 7,
name: "word19",
id: 219062
},
{
level: 7,
name: "word20",
id: 222243
}
]
}
]
}
]
}
]
}
]
},
{
level: 2,
name: "word3",
id: 214266,
_children: [
{
level: 3,
name: "word11",
id: 216675
},
{
level: 3,
name: "word12",
id: 216671
}
]
}
]
}
]
}
];
const idList = [214659];
const found = _.filterDeep(
data,
function(value) {
return _.indexOf(idList, value.id) !== -1;
},
{ tree: true, childrenPath: '_children' }
);
console.log(found);
<script src="https://cdn.jsdelivr.net/npm/lodash/lodash.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/deepdash/browser/deepdash.min.js"></script>
<script>
deepdash(_);
</script>
Here is a recursive function that will find the parent and/or children matching a condition.
In this example, the parent item will always be displayed if a child item is a match - even if the parent itself is not a match - but you can easily adjust the code to your needs by tuning the test in the for loop.
var filterTree = function (data, filter) {
if (data['_children'] && data['_children'].length > 0) {
for (var i in data['_children']) {
return data[filter.field] == filter.value || filterTree(data['_children'][i], filter);
}
}
return data[filter.field] == filter.value;
};
Call this function as a custom filter callback:
table.setFilter(filterTree, {field:'myfield', type:'=', value:'myvalue'});
Note that this is just example code that focuses on the logic of filtering a tree recursively. The above works only for the '=' comparison.
In a real situation, you will have to implement more code to handle all other operators supported by tabulator, as dynamic operator assignment is not possible in Javascript. You could maybe consider eval() but that's another story.
More info about dynamic operator assignment here:
Are Variable Operators Possible?
Here is an example of implementation handling all tabulator operators:
// Operators
var compare = {
'=': function(a, b) { return a == b },
'<': function(a, b) { return a < b },
'<=': function(a, b) { return a <= b },
'>': function(a, b) { return a > b },
'>=': function(a, b) { return a >= b },
'!=': function(a, b) { return a != b },
'like': function(a, b) { return a.includes(b)}
};
// Filter function
var filterTree = function (data, filter) {
if (data['_children'] && data['_children'].length > 0) {
for (var i in data['_children']) {
return compare[filter.type](data[filter.field], filter.value) || filterTree(data['_children'][i], filter);
}
}
return compare[filter.type](data[filter.field], filter.value);
};
// Set a filter. The operator can now be provided dynamically
table.setFilter(filterTree, {field:'myfield', type: '>=', value:'myvalue'});

What's the proper way to invoke actions in XState FMS?

My definition for XState FMS in the file task_statemachine.js is as follows:
module.exports = {
id: 'runner',
initial: 'setup',
states: {
setup: {
on: {
RECEIVED: {
target: 'running',
actions: 'runTask',
},
ERROR: {
target: 'error',
actions: 'taskError',
},
TERMINATED: {
target: 'terminated',
actions: 'terminate',
},
},
},
running: {
on: {
COMPLETE: {
target: 'complete',
actions: 'taskComplete',
},
ERROR: {
target: 'error',
actions: 'taskError',
},
TERMINATED: {
target: 'terminated',
actions: 'terminate',
},
},
},
terminated: {
type: 'final',
},
complete: {
type: 'final',
},
error: {
type: 'final',
},
},
}
The actual machine itself and service are created in the constructor of the TASK() class like this:
if (!this.state) this.state = interpret(Machine(require('./task_statemachine'), {
actions: {
runTask: this.runTask,
taskComplete: this.taskComplete,
taskError: this.taskError,
terminate: this.terminate
}
})).start();
I have a problem when trying to run actions which are supposed to call the functions defined in the class. I'm sending the events in the following way this.state.send('COMPLETE');
If I define actions as array of callbacks, like this runTask: this.runTask() the actions seemingly run as they should. According to my colleagues it's a bad practice to do so. What the correct way to invoke the actions once the class in loaded?
The issue was caused by this binding. The solution was to warp the function callback into an arrow function in the actions array.
if (!this.state) this.state = interpret(Machine(require('./task_statemachine'), {
actions: {
// High Level Functions
runTask: () => {
this.runTask()
},
taskComplete: () => {
this.taskComplete()
},
taskError: () => {
this.taskError()
},
terminate: () => {
this.terminate()
}
// runTask() Functions
}
})).start();

How to search part of a sentece in elasticsearch

I'm using Elasticsearch js to make a search engine, like so:
let jobs = await client.search({
index: 'index',
type: 'doc',
body: {
query: {
bool: {
must: [
{
match: {
title: 'test'
}
}
]
}
}
}
});
if the title has 'test' in it , it will show, but when it has something like 'hello this is/test' it wont show up, how do I fix it?
You can surround the string with *:
let jobs = await client.search({
index: 'index',
type: 'doc',
body: {
query: {
bool: {
must: [
{
match: {
title: '*test*'
}
}
]
}
}
}
});

Resources