Can node-opcua sever return values dynamically bases on the user context? - node-opcua

In my use case, I have to return different values based on the user context / current session in the server.
In pseudo-node-opcua code something like following:
namespace.addVariable({
componentOf: device,
browseName: "MyVariable1",
dataType: "Double",
value: {
get: context => {
const user = context.userIdentity;
const value = user === "admin"
? service.getRootValue()
: service.getUserValueFor(user);
return new Variant({dataType: DataType.Double, value })
}
}
});
Is there a way how to achieve this with node-opcua?

One can override the readValue function, which takes the context as a parameter, to return a dynamic value:
myVariable1.readValue = (context, indexRange, dataEncoding) => (
new DataValue({
value: {
dataType: DataType.String,
value: `Hello, ${context.session.userIdentityToken.userName}!`,
statusCode: StatusCodes.Good
}
})
);

Related

Cloud Functions: Transaction with FieldValue.increment() not running atomically

I have a Cloud Functions transaction that uses FieldValue.increment() to update a nested map, but it isn't running atomically, thus the value updates aren't accurate (running the transaction in quick succession results in an incorrect count).
The function is fired via:
export const updateCategoryAndSendMessage= functions.firestore.document('events/{any}').onUpdate((event, context) => {
which include the following transaction:
db.runTransaction(tx => {
const categoryCounterRef = db.collection("data").doc("categoryCount")
const intToIncrement = event.after.data().published == true ? 1 : -1;
const location = event.after.data().location;
await tx.get(categoryCounterRef).then(doc => {
for (const key in event.after.data().category) {
event.after.data().category[key].forEach(async (subCategory) => {
const map = { [key]: { [subCategory]: FieldValue.increment(intToIncrement) } };
await tx.set(categoryCounterRef, { [location]: map }, { merge: true })
})
}
},
).then(result => {
console.info('Transaction success!')
})
.catch(err => {
console.error('Transaction failure:', err)
})
}).catch((error) => console.log(error));
Example:
Value of field to increment: 0
Tap on button that performs the function multiple times in quick succession (to switch between true and false for "Published")
Expected value: 0 or 1 (depending on whether reference document value is true or false)
Actual value: -3, 5, -2 etc.
As far as I'm aware, transactions should be performed "first come, first served" to avoid inaccurate data. It seems like the function isn't "queuing up" correctly - for lack of a better word.
I'm a bit stumped, would greatly appreciate any guidance with this.
Oh goodness, I was missing return...
return db.runTransaction(tx => {

Access mongoose parent document for default values in subdocument

I have a backend API for an Express/Mongo health tracking app.
Each user has an array of weighIns, subdocuments that contain a value, a unit, and the date recorded. If no unit is specified the unit defaults to 'lb'.
const WeighInSchema = new Schema({
weight: {
type: Number,
required: 'A value is required',
},
unit: {
type: String,
default: 'lb',
},
date: {
type: Date,
default: Date.now,
},
});
Each user also has a defaultUnit field, that can specify a default unit for that user. If that user posts a weighIn without specifying a unit, that weighIn should use the user's defaultUnit if present or else default to 'lb'.
const UserSchema = new Schema({
email: {
type: String,
unique: true,
lowercase: true,
required: 'Email address is required',
validate: [validateEmail, 'Please enter a valid email'],
},
password: {
type: String,
},
weighIns: [WeighInSchema],
defaultUnit: String,
});
Where is correct location for this logic?
I can easily do this in the create method of my WeighInsController, but this seems at best not best practice and at worst an anti-pattern.
// WeighInsController.js
export const create = function create(req, res, next) {
const { user, body: { weight } } = req;
const unit = req.body.unit || user.defaultUnit;
const count = user.weighIns.push({
weight,
unit,
});
user.save((err) => {
if (err) { return next(err); }
res.json({ weighIn: user.weighIns[count - 1] });
});
};
It doesn't seem possible to specify a reference to a parent document in a Mongoose schema, but I would think that a better bet would be in my pre('validate') middleware for the subdocument. I just can't see a way to reference the parent document in the subdocument middleware either.
NB: This answer does not work as I don't want to override all of the user's WeighIns' units, just when unspecified in the POST request.
Am I stuck doing this in my controller? I started with Rails so I have had 'fat models, skinny controllers' etched on my brain.
You can access the parent (User) from a sub-document (WeighIn) using the this.parent() function.
However, I'm not sure if it's possible to add a static to a sub-document, so that something like this would be possible:
user.weighIns.myCustomMethod(req.body)
Instead, you could create a method on the UserSchema, like addWeightIn:
UserSchema.methods.addWeightIn = function ({ weight, unit }) {
this.weightIns.push({
weight,
unit: unit || this.defaultUnit
})
}
Then just call the user.addWeightIn function within your controller and pass the req.body to it.
This way, you get 'fat models, skinny controllers'.

Implementing pagination in vanilla GraphQL

Every tutorial I have found thus far has achieved pagination in GraphQL via Apollo, Relay, or some other magic framework. I was hoping to find answers in similar asked questions here but they don't exist. I understand how to setup the queries but I'm unclear as to how I would implement the resolvers.
Could someone point me in the right direction? I am using mongoose/MongoDB and ES5, if that helps.
EDIT: It's worth noting that the official site for learning GraphQL doesn't have an entry on pagination if you choose to use graphql.js.
EDIT 2: I love that there are some people who vote to close questions before doing their research whereas others use their knowledge to help others. You can't stop progress, no matter how hard you try. (:
Pagination in vanilla GraphQL
// Pagination argument type to represent offset and limit arguments
const PaginationArgType = new GraphQLInputObjectType({
name: 'PaginationArg',
fields: {
offset: {
type: GraphQLInt,
description: "Skip n rows."
},
first: {
type: GraphQLInt,
description: "First n rows after the offset."
},
}
})
// Function to generate paginated list type for a GraphQLObjectType (for representing paginated response)
// Accepts a GraphQLObjectType as an argument and gives a paginated list type to represent paginated response.
const PaginatedListType = (ItemType) => new GraphQLObjectType({
name: 'Paginated' + ItemType, // So that a new type name is generated for each item type, when we want paginated types for different types (eg. for Person, Book, etc.). Otherwise, GraphQL would complain saying that duplicate type is created when there are multiple paginated types.
fields: {
count: { type: GraphQLInt },
items: { type: new GraphQLList(ItemType) }
}
})
// Type for representing a single item. eg. Person
const PersonType = new GraphQLObjectType({
name: 'Person',
fields: {
id: { type: new GraphQLNonNull(GraphQLID) },
name: { type: GraphQLString },
}
})
// Query type which accepts pagination arguments with resolve function
const PersonQueryTypes = {
people: {
type: PaginatedListType(PersonType),
args: {
pagination: {
type: PaginationArgType,
defaultValue: { offset: 0, first: 10 }
},
},
resolve: (_, args) => {
const { offset, first } = args.pagination
// Call MongoDB/Mongoose functions to fetch data and count from database here.
return {
items: People.find().skip(offset).limit(first).exec()
count: People.count()
}
},
}
}
// Root query type
const QueryType = new GraphQLObjectType({
name: 'QueryType',
fields: {
...PersonQueryTypes,
},
});
// GraphQL Schema
const Schema = new GraphQLSchema({
query: QueryType
});
and when querying:
{
people(pagination: {offset: 0, first: 10}) {
items {
id
name
}
count
}
}
Have created a launchpad here.
There's a number of ways you could implement pagination, but here's two simple example resolvers that use Mongoose to get you started:
Simple pagination using limit and skip:
(obj, { pageSize = 10, page = 0 }) => {
return Foo.find()
.skip(page*pageSize)
.limit(pageSize)
.exec()
}
Using _id as a cursor:
(obj, { pageSize = 10, cursor }) => {
const params = cursor ? {'_id': {'$gt': cursor}} : undefined
return Foo.find(params).limit(pageSize).exec()
}

Custom Postgresql Sequence Nextval in SequelizeJS

How can i define sequence nextval() in sequelizeJS Model.
Is there any predefined function available for nextval() in sequelizeJS ?,
Is there any possiblity to write sequence nextval() custom function inside sequelize define model ?
In case there is someone at this point who wants to do this as well, the best way to do it I guess is using Sequelize.literal:
// on your model
const model = sequelize.define("model", {
...attributes,
customSequence: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: sequelize.Sequelize.literal("nextval('custom_sequence')")
},
})
// after create a migration for your new column
module.exports = {
up: async (queryInterface, sequelize) => {
await queryInterface.sequelize.query("CREATE SEQUENCE custom_sequence start 1020 increment 20",)
await queryInterface.addColumn('table', 'customSequence', {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: sequelize.Sequelize.literal("nextval('custom_sequence')")
})
},
down: async (queryInterface, sequelize) => {
await queryInterface.sequelize.query('DROP SEQUENCE custom_sequence')
await queryInterface.removeColumn('table', 'customSequence')
}
};
Currently there is no way in sequelize to do so, kindly refer
https://github.com/sequelize/sequelize/issues/3555
No predefined function for that. But you can make use of raw query and get nextval like
sequelize.query("SELECT nextval('sequence')", {
type: collection.Sequelize.QueryTypes.SELECT
});
I was able to do something similar as posted by #sebin and chaining the promise to my model.create(), like so:
this.sequelize
.query("SELECT max(cust_no) + 1 as 'custNo' from employees.customers", {
type: this.sequelize.Sequelize.QueryTypes.SELECT
})
.then(results => {
return results[0];
})
.then(nextPk => {
const values = Object.assign({}, newCustomer, nextPk);
return customersModel.create(values);
})
.then(newRecord => {
console.log('newRecord:', newRecord);
});
In the above case I use max +1 but the solution applies to your sequence all the same.
This works perfectly with MSSQL / MariaDB
Sequelize.literal in the "defaultValue" was the key.
I'm new to Sequelize, and luckily it lets me redefine the "id" field, which normally doesn't have to be specified in the model.
Happy day for me.
module.exports = (sequelize, Sequelize) => {
const People = sequelize.define("people", {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
allowNull: false,
defaultValue: sequelize.Sequelize.literal("NEXT VALUE FOR dbo.BAS_IDGEN_SEQ")
},...

How can I validate a model attribute against another model attribute in Sails?

Let's say I have an Invoice model in SailsJS. It has 2 date attributes: issuedAt and dueAt. How can I create a custom validation rule that check that the due date is equal or greater than the issued date?
I tried creating a custom rule, but it seems I cannot access other properties inside a rule.
module.exports = {
schema: true,
types: {
duedate: function(dueAt) {
return dueAt >= this.issuedAt // Doesn't work, "this" refers to the function, not the model instance
}
},
attributes: {
issuedAt: {
type: 'date'
},
dueAt: {
type: 'date',
duedate: true
}
}
};
I hope you found a solution now, but for those interested to a good way to handle this i will explain my way to do it.
Unfortunatly as you said you can't access others record attributes in attribute customs validation function.
#Paweł Wszoła give you the right direction and here is a complete solution working for Sails#1.0.2 :
// Get buildUsageError to construct waterline usage error
const buildUsageError = require('waterline/lib/waterline/utils/query/private/build-usage-error');
module.exports = {
schema: true,
attributes: {
issuedAt: {
type: 'ref',
columnType: 'timestamp'
},
dueAt: {
type: 'ref',
columnType: 'timestamp'
}
},
beforeCreate: (record, next) => {
// This function is called before record creation so if callback method "next" is called with an attribute the creation will be canceled and the error will be returned
if(record.dueAt >= record.issuedAt){
return next(buildUsageError('E_INVALID_NEW_RECORD', 'issuedAt date must be equal or greater than dueAt date', 'invoice'))
}
next();
}
};
beforeCreate method in model as first param takes values. The best place for this kind of validation I see here.
beforeCreate: (values, next){
if (values.dueAt >= values.issuedAt) {
return next({error: ['...']})
}
next()
}