How to add more functions in the Helper class? - sails.js

According to Sails's document, helper class has a default 'fn' function. I cannot add function2 in it. Can you help me? Thankyou!
fn: async (inputs, exits) => {
var result = inputs.a + inputs.b;
return exits.success(result);
},
function2 : async (a, b) => {
var result = a + b;
return exits.success(result);
},

An helper and controllers(actions) can be writing using "actions2" syntax. I didn't found documentation about it's specification but according to documentation look like this: https://sailsjs.com/documentation/concepts/actions-and-controllers#?actions-2
Alternatives to declare a function
You could add a function outside helper or controllers(actions) like this.
function add(a, b) {
return a + b;
}
module.exports = {
friendlyName: 'Add - math operation for two numbers',
description: 'Add two numbers',
inputs: {
a: {
description: 'first value',
required: true
},
b: {
description: 'second value',
required: true
}
},
exits: {
},
fn: async function (inputs, exits) {
// call to add function
return exits.success({result: add(inputs.a, inputs.b)});
}
};
Other options inside fn attribute
module.exports = {
friendlyName: 'Add - math operation for two numbers',
description: 'Add two numbers',
inputs: {
a: {
description: 'first value',
required: true
},
b: {
description: 'second value',
required: true
}
},
exits: {
},
fn: async function (inputs, exits) {
function add(a, b) {
return a + b;
}
// call to add function
return exits.success({result: add(inputs.a, inputs.b)});
}
};

To add a new helper to the helpers class, you would define it in a new file. In the console you can use sails generate helper <desired extension>/<desired helper name> This will give you a whole file with its own inputs, exits and fn. As an example I might have a set of helpers that work with a specific api called "formtastic". To create a helper that retrieves forms, in the terminal sails generate helper formtastic/retrieve-forms. This will create a new file that I can specify the inputs and exits and write all the needed code. Then when I want to call this helper elsewhere in back end code, I use camel case and dots for each folder extension, so: sails.helpers.formtastic.retrieveForms()

Related

How to listen to events to sort off-chain

I read this article on sorting an array off-chain using event listeners, but my array doesn't appear to be sorted in time when the array is called in typescript.
This is my contract:
pragma solidity 0.8.4;
pragma experimental ABIEncoderV2;
contract Test {
uint256[] unsortedArr;
uint256[] sortedArr;
event sort(uint256[]);
function addElement(uint256 element) public {
unsortedArr.push(element);
}
function sortOffChain() public returns (uint[] memory)
{
/*On the backend, the algorithm would look something like this -
*event listener listening for sort()
*calls sort function in typescript upon getting triggered
*calls getSortedData() function with returned value of sort()
*/
emit sort(unsortedArr);
// I want sortedArr below to be sorted by the time the return is called
return sortedArr;
}
function getSortedData(uint256[] memory sortedData) public
{
sortedArr = sortedData;
}
// for typescript test
function getUnsortedArr() public view returns (uint256[] memory) {
return unsortedArr;
}
function getSortedArr() public view returns (uint256[] memory) {
return sortedArr;
}
}
This is what the event listener looks like in TypeScript:
async function main() {
;
// deploying
const Test = await hre.ethers.getContractFactory("Test");
const test = await Test.deploy();
await test.deployed();
console.log("Test deployed to", test.address);
// add elements to array
await test.addElement(5);
await test.addElement(3);
await test.addElement(7);
await test.addElement(2);
await test.addElement(1);
await test.addElement(9);
// event listener
const OffChainResult = await test.sortOffChain();
test.on("sort", (arr: []) => {
let arrayForSort = [...arr];
const sortedArr = arrayForSort.sort((n1: number,n2 : number) => n1 - n2);
console.log("array sorted");
test.getSortedData(sortedArr);
console.log("getSortedData completed");
}
);
console.log("output of getSortedData:");
console.log(OffChainResult);
This is what comes out in the terminal when I run the script:
Test deployed to 0x5FbDB2315678afecb367f032d93F642f64180aa3
output of getSortedData:
{
hash: '0x40eeca2775e29300e51f3a067c99195f0cf33ee0aac73eaeaf05d826b41c3d4c',
type: 2,
accessList: [],
blockHash: '0xd952eb1b086de3b1ee213fff01b464925670bc6d7e515ac3f7dad3b732471857',
blockNumber: 8,
transactionIndex: 0,
confirmations: 1,
from: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
gasPrice: BigNumber { value: "1346048670" },
maxPriorityFeePerGas: BigNumber { value: "1000000000" },
maxFeePerGas: BigNumber { value: "1692097340" },
gasLimit: BigNumber { value: "29021272" },
to: '0x5FbDB2315678afecb367f032d93F642f64180aa3',
value: BigNumber { value: "0" },
nonce: 7,
data: '0xb8dbac2c',
r: '0x621282eec4ac159b052250faa62aa5e954510ba91e841d3477bf0228ecf952d9',
s: '0x7d36e86d604b58006dd918efc52babd3f3e61dd9f80e36fa2e3e8a9a03fe5298',
v: 1,
creates: null,
chainId: 31337,
wait: [Function (anonymous)]
}
array sorted
getSortedData completed
There are two issues: (1) getSortedData() which is called by sortOffChain() returns a value before the event listener gets a chance to sort the array and set sortedArr to the right value and (2) even if the array isn't sorted, sortOffChain() should return an empty array, not whatever the console output for `OffChain.
Is this just an issue with how I made the event listener? Do I need to put the lines of code in a different order? Also, what is the thing that the console output for OffChainResult.
I've also heard that the sorted array that is to be given to the smart contract can't be validated. How do I ensure that it is?

AWS CDK parameter error: Value of property Parameters must be an object with String (or simple type) properties

Update
The accepted answer bellow by Victor is the correct workaround.
I get in contact with AWS Support that confirmed the bug. They are aware of the issue and linked me to those 2 links to follow the case.
... During my investigation and replication of the issue, I noticed that this issue seems to be with how CDK handles the VPCEndpoint resource as its parameters include a list of DNS entries with them.
This is a known issue with the resource:
https://github.com/aws/aws-cdk/issues/5897
https://github.com/aws/aws-cdk/issues/9488
Until then if you face this problem, the solution is here.
Original question
I was expecting to be able to use Fn.select and Fn.split out of the box, but the bellow code have some very weird behavior, wondering if I'm doing something unexpected.
I expected the output to contains the declared functions fn::Select [1, fn:split [ fn:select [0, getAttr [ something ] ] ] ]
But actually what I see is just getAttr [something], what leads to Value of property Parameters must be an object with String (or simple type) properties during deployment
Any help is appreciated
Here's the code to reproduce the error
#!/usr/bin/env node
import 'source-map-support/register';
import {App, aws_ec2, aws_lambda, Fn, NestedStack, NestedStackProps, Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
const app = new App();
export class MainStack extends Stack {
constructor(scope: Construct, id: string, props: StackProps) {
super(scope, id, props);
new Nested1(this, 'first', {})
}
}
export class Nested1 extends NestedStack {
constructor(scope: Construct, id: string, props: NestedStackProps) {
super(scope, id, props);
const vpc = new aws_ec2.Vpc(this, 'vpc', {
subnetConfiguration: [{
cidrMask: 28,
name: 'private-test-bug',
subnetType: aws_ec2.SubnetType.PRIVATE_ISOLATED,
}]
})
const apiEndpoint = new aws_ec2.InterfaceVpcEndpoint(this, `apiEndpoint`, {
service: {
name: 'com.amazonaws.eu-central-1.execute-api',
port: 443,
},
vpc: vpc,
subnets: {
subnets: vpc.isolatedSubnets,
onePerAz: true
},
open: true,
privateDnsEnabled: false,
});
// BUG IS HERE
new Nested2(this, 'nested2', { VpcEndpoint: apiEndpoint }) // CDK should handle and parse the reference, but it dont
}
}
export interface Nested2StackProps extends NestedStackProps { VpcEndpoint: aws_ec2.InterfaceVpcEndpoint; }
export class Nested2 extends NestedStack {
constructor(scope: Construct, id: string, props: Nested2StackProps) {
super(scope, id, props);
const lambda = new aws_lambda.Function(this, 'lambda-function', {
code: aws_lambda.Code.fromInline(`exports.handler = (event, context) => { console.log(process.env.dns_name) }`),
handler: 'index.handler',
runtime: aws_lambda.Runtime.NODEJS_16_X,
environment: { dns_name: Fn.select(1, Fn.split(':', Fn.select(0, props.VpcEndpoint.vpcEndpointDnsEntries))) } // USAGE IS HERE
})
}
}
new MainStack (app, 'TestStack', {
env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION },
});
In a nutshell, it looks like a bug in the CDK. The CDK tries to send an array as a parameter to the nested template. Instead, it should join the array and pass it as a string.
I suggest a workaround for the issue. I split my code into two parts. First, I explain the idea and show some code snippets. Finally, I show the complete solution.
We define parameter in the nested stack like this:
const stack = new NestedStack(scope, id, {
parameters: {
VpcEndpointDnsEntries: props.VpcEndpointDnsEntries
},
})
In the code, we create an object for this parameter and use it to get the parameter value.
const endpoints = new CfnParameter(stack, 'VpcEndpointDnsEntries', {
type: 'List<String>',
description: 'List of entries.'
})
// Read the value:
const strings = endpoints.valueAsList
In the final step, we pass parameters to the nested stack almost as usual, except that we join the string.
createNestedStack(stack, 'NestedStack',
{ VpcEndpointDnsEntries: Fn.join(',', apiEndpoint.vpcEndpointDnsEntries) })
Note that the lambda function is not working, it throws a runtime exception. Otherwise, the stack and nested stack deploys normally.
Please find the complete code below.
Sorry, I am really in a hurry now. I plan to fix grammar and update the answer this evening.
import { App, CfnParameter, Fn, NestedStack, Stack } from 'aws-cdk-lib'
import { env } from 'process'
import { InterfaceVpcEndpoint, Vpc } from 'aws-cdk-lib/aws-ec2'
import { Code, Function, Runtime } from 'aws-cdk-lib/aws-lambda'
function createMainStack (scope, id, props) {
const stack = new Stack(scope, id, props)
const vpc = Vpc.fromLookup(stack, 'Vpc', { vpcName: 'WarehousePrinters' })
const apiEndpoint = new InterfaceVpcEndpoint(stack, `ApiEndpoint`, {
service: {
name: 'com.amazonaws.eu-west-1.execute-api',
port: 443,
},
vpc,
subnets: { subnets: vpc.privateSubnets, onePerAz: true },
open: true,
privateDnsEnabled: false,
})
createNestedStack(stack, 'NestedStack', { VpcEndpointDnsEntries: Fn.join(',', apiEndpoint.vpcEndpointDnsEntries) })
return stack
}
function createNestedStack (scope, id, props) {
const stack = new NestedStack(scope, id, {
parameters: {
VpcEndpointDnsEntries: props.VpcEndpointDnsEntries
},
})
const endpoints = new CfnParameter(stack, 'VpcEndpointDnsEntries', {
type: 'List<String>',
description: 'List of entries.'
})
new Function(stack, 'LambdaFunction', {
code: Code.fromInline(`export function handler = () => console.log(env.dns_name)`),
handler: 'index.handler',
runtime: Runtime.NODEJS_16_X,
environment: {
dns_name: Fn.select(1, Fn.split(':', Fn.select(0, endpoints.valueAsList)))
}
})
return stack
}
const app = new App()
createMainStack(app, 'MainStack', {
env: { account: env.CDK_DEFAULT_ACCOUNT, region: env.CDK_DEFAULT_REGION }
})

how to get query parameter in action2 in sails

I dont know how to set up route according to REST for the search request url like this
localhost:1337/system?id=10
After that, how can we get parameter from the above request in action 2 like this
action2(inputs,exits)
I want to follow best practice for web api in sails. Please hint me to solve and recommend any handy documents.
Thanks in advance
For the Actions2 format, you'd use inputs.id, after properly defining the inputs, of course. For more information, consult the documentation on Actions and Controllers. This would need more finishing, but it gets your input and a function. You'd want to add in your exits, and some real logic, of course, and probably some decent descriptions.
module.exports = {
inputs: {
id: {
type: 'number',
required: true
}
},
exits: {
success: { ... },
error: { ... }
},
fn: async function (inputs, exits) {
var myId = inputs.id;
return exits.success(myId);
}
}

Custom Select() with parameter

UPDATE
As of #NGXS v3.1, they finally introduced arguments into #Selector().
https://www.ngxs.io/concepts/select#lazy-selectors
Examples from the DOCS
First, you define the #Selector "pandas"
#State<string[]>({
name: 'animals',
defaults: []
})
#Injectable()
export class ZooState {
#Selector()
static pandas(state: string[]) {
return (type: string) => {
return state.filter(s => s.indexOf('panda') > -1).filter(s => s.indexOf(type) > -1);
};
}
}
Then you just call it in your '.ts' file
import { Store } from '#ngxs/store';
import { map } from 'rxjs/operators';
#Component({ ... })
export class ZooComponent {
babyPandas$: Observable<string[]>;
constructor(private store: Store) {
this.babyPandas$ = this.store
.select(ZooState.pandas)
.pipe(map(filterFn => filterFn('baby')));
}
}
* From Old Post *
I am trying to create a custom #Select () to be able to drill down a particular tree and return the values dynamically. Getting either undefined or it's not making it (executing)
user.component.ts
const location = 'new york'
#Select(state => UserState.getUserLocationSlots(state, location)) slots$;
user.state.ts
#Selector()
static getUserLocationSlots(state: UserStateModel, location: any) {
console.log(state);
console.log(location); // <-- expecting 'new york', but getting undefined
}
You can achieve this by using crateSelector function from #ngxs/store
In your .state.ts file:
static getLocationSlots(location: string) {
return createSelector([UserState], (state: string[) => {
// logic for filtering your data
// eg.: state.filter(element => element == location)
})
}
In your .component.ts file:
#Select(UserState.getLocationSlots('new york')) slots$: Observable<any>
You can also check here for more details
I don't think it is possible to pass parameter to #Selector() decorated functions in ngxs v2. It would be nice though.
A ticket exist for this feature request.
Also, I think you are not using #Selector() correctly. I should be something like (hence, cannot pass parameters):
#Select(UserState.getUserLocationSlots) slots$
Refer to the docs.
Note: I am not an expert in ngxs...this is just based on what I understand now.
This is achievable in NGXS v2 & v3. Copied from my comment in the discussion on dynamic selectors here
We can achieve this at the moment using a pattern often used for redux
selectors...
The #Selector decorator can be written so that it returns a function
with the desired parameter. This enables the desired dynamic selector
arguments as well as late resolution of the selected state. For
Example:
#State<UserStateModel>( ... )
export class UserState {
#Selector()
getFilteredUsersFn(userStateModel: UserStateModel) {
return (filter: string) =>
userStateModel.users.filter((user) => user.indexOf(filter) >= 0);
}
}
And then the component would contain:
#Component({...})
export class AppComponent {
#Select(UserState.getFilteredUsersFn)
filteredUsersFn$: Observable<(filter: string) => User[]>;
get currentFilteredUsers$() {
return this.filteredUsersFn$
.pipe(map(filterFn => filterFn('myFilter')));
}
}
To pass parameters you can have the select return a function, it isn't elegant, however it works.
For example the select statement would look like:
#Selector()
static getItemByIdFn(state: { [id: number]: Entity }) {
return (id: number) => {
return state[id];
};
}
then in the component:
this.store.select(MyState.getItemByIdFn)
.pipe(map(mapByIdFn) => mayByIdFn(1)) // using the returned function
.subscribe(...);
Note the map, which is where you pass your id to the returned function. Here you can place whatever parameters you would like.
Hope this helps :)!

Is there an efficient way to assert that all the elements of a PageObject are visible?

It's great how you can describe key elements of a page using the elements object in nightwatch's PageObject.
A very basic test of "is the page rendered correctly?" seems to consist of a string of asserts that every element is visible:
'Does it look right' : function (browser) {
browser.page.welcome()
.navigate()
.assert.title('My App')
.assert.visible('#header')
.assert.visible('#usernameField')
.assert.visible('#passwordField')
.assert.visible('#forgotPasswordLink')
.assert.visible('#signupButton')
Is there a more efficient way to do this?
You can run a script with your custom logic and then test against that
var script = function () {
//some logic or query selector test
return foo;
};
module.exports = {
'Test PCC': function (browser) {
var test;
browser
url('foo')
.waitForElementVisible('body', 1000)
.execute(script, [], function (response) {
console.log(response);
test = response.value;
})
//assert. test is something
.end();
}
};