Loopback is not responding with modified data after 'loaded' hook - javascript

I have the following code in my Model.js file.
Model.observe('loaded', (ctx, next) => {
const {
data,
options: {
user
}
} = ctx;
const owner = (user && data && user.uid === data.userId) || false;
console.log(
`${data.id}: loaded - access by ${user && user.name}, owner:${owner}`
);
if (!owner) {
delete data.testProp1;
}
console.log('returning: ', ctx.data);
next();
});
When I make a request, I see the following log output (server logs):
f3f9ffd6-14dc-42e5-94ba-503aa3426faa: loaded - access by User1, owner:false
returning:
{
testProp2: true,
id: 'f3f9ffd6-14dc-42e5-94ba-503aa3426faa',
userId: 'sfeywkKSuBTlf0DwE4ZOFd8RX5E3'
}
But then in the actual response the browser receives I actually get:
{
testProp1: true,
testProp2: true,
id: 'f3f9ffd6-14dc-42e5-94ba-503aa3426faa',
userId: 'sfeywkKSuBTlf0DwE4ZOFd8RX5E3'
}
Is there something in the documentation I am missing? Deleting the property is exactly what it shows in the Loopback docs here. Also, I actually see the modified data as the data property on the ctx object before calling next(). Anyone run into this issue or know some caveat to the docs that isn't explicitly stated?

Related

Handling dependent react queries

I have two react queries in the same component
const { data: sdrData, status: sdrDataLoading } = useQuery(
[queryInfo[0]?.dcsSysId, data[0]?.dcsStructSysId, data[cardIndex]?.dcsStructNodeId],
() => getSDR(queryInfo[0]?.dcsSysId, data[0]?.dcsStructSysId, data[cardIndex]?.dcsStructNodeId),
);
const { isIdle, data: sdrTemplateData, status: sdrTemplateDataLoading } = useQuery(
[sdrData[0]?.dcsSdrSysId, queryInfo[0]?.fldTemplateSysId],
() =>
SDRTemplateValues(sdrData[0]?.dcsSdrSysId, queryInfo[0]?.fldTemplateSysId, {
// The query will not execute until the userId exists
enabled: !!sdrData[0]?.dcsSdrSysId,
retry: true,
}),
);
My second query is depended on the first I need to access sdrData[0] for the first arg in my query however when I do this the query is undefined initially and it fails. Is there a good way to handle this. I saw you can set it equal to a variable, but I'm still faced with the same problem.
I need a way to tell the first query to wait until the second query is finished before it tried to access the arguments. I thought you could set enabled like I did, but that didn't work either.
I was able to solve this by adding the data parameter I need to the beginning of the argument array like this.
const { data: sdrData, status: sdrDataLoading } = useQuery(
queryInfo[0]?.dcsSysId && [
queryInfo[0]?.dcsSysId,
data[0]?.dcsStructSysId,
data[cardIndex]?.dcsStructNodeId,
],
() => getSDR(queryInfo[0]?.dcsSysId, data[0]?.dcsStructSysId, data[cardIndex]?.dcsStructNodeId),
);
const { data: sdrTemplateData, status: sdrTemplateDataLoading } = useQuery(
sdrData?.[0]?.dcsSdrSysId && [sdrData?.[0]?.dcsSdrSysId, queryInfo[0]?.fldTemplateSysId],
() => SDRTemplateValues(sdrData?.[0]?.dcsSdrSysId, queryInfo[0]?.fldTemplateSysId),
);
You are on the right path here.
You just need to check if sdrData is undefined. You are trying to access the first element (sdrData[0]) but initially it's undefined.
...
{
enabled: !!(sdrData && sdrData[0]?.dcsSdrSysId
}
...

Feathers-mongoose : Get by custom attribute in feathers-mongoose

I have a very basic feathers service which stores data in mongoose using the feathers-mongoose package. The issue is with the get functionality. My model is as follows:
module.exports = function (app) {
const mongooseClient = app.get('mongooseClient');
const { Schema } = mongooseClient;
const messages = new Schema({
message: { type: String, required: true }
}, {
timestamps: true
});
return mongooseClient.model('messages', messages);
};
When the a user runs a GET command :
curl http://localhost:3030/messages/test
I have the following requirements
This essentially tries to convert test to ObjectID. What i would
like it to do is to run a query against the message attribute
{message : "test"} , i am not sure how i can achieve this. There is
not enough documentation for to understand to write or change this
in the hooks. Can some one please help
I want to return a custom error code (http) when a row is not found or does not match some of my criterias. How can i achive this?
Thanks
In a Feathers before hook you can set context.result in which case the original database call will be skipped. So the flow is
In a before get hook, try to find the message by name
If it exists set context.result to what was found
Otherwise do nothing which will return the original get by id
This is how it looks:
async context => {
const messages = context.service.find({
...context.params,
query: {
$limit: 1,
name: context.id
}
});
if (messages.total > 0) {
context.result = messages.data[0];
}
return context;
}
How to create custom errors and set the error code is documented in the Errors API.

How to know which params is missing in a query?

I'm setting up a Booking router in NodeJS, and I have many params in.
Now when I forgot params I return an error like :
500: Need more information
I wonder if it's possible to know which params are missing when I return the error code.
This is for a new API made in NodeJS
Here are the params that I want to retrieve from the front ( made in ReactJS )
let body = {
agentDutyCode: "STRING",
RatePlanCode: params.rateCode,
RoomCode: params.roomCode,
AmountAfterTax: params.amountTax,
Start: params.fromDate,
End: params.toDate,
CardCode: params.cardCode,
CardNumber: params.cardNumber,
ExpireDate: params.expireDate,
SeriesCode: params.cvv,
CardHolderName: params.nameCard,
ChainCode: params.chainCode,
HotelCode: params.hotelCode,
RoomQuantities: params.roomQuantities,
GuestQuantitie: params.numberGuest,
GuestPerRoom: params.guestPerRoom,
LastName: params.lastName,
FirstName: params.firstName,
PhoneNumber: params.phoneNumber,
email: params.email,
FVL_SUBUNIT_7: params.walletAddress
}
And this is my promise :
cdsJson.bookResource(req.body)
.then((response) => {
if (response !== null) {
res.response = {
...response
}
} if (response.hotel.length === 0) {
res.respStatus = 500
res.response = {
sendMsg: "Need more informations"
}
next('route')
}
return response
})
If the request succeeds I got a reservation ID otherwise I got :
Error 500: Need more information
Read the documentation or the source code.
Seriously. If the API response doesn't tell you in the error message, then there is no way to know what parameters it expects programmatically.
try it for a for ... in loop like this:
cdsJson.bookResource(req.body)
.then((response) => {
if (response !== null) {
res.response = {
...response
}
} if (response.hotel.length === 0) {
res.respStatus = 500
let errorStr = "Need more informations"
for(var key in req.body) { // Get all parameters that are not set
if(objects[key] == undefined)
errorStr += "\nParameter ["+key+"] is missing!"
}
res.response = {
sendMsg: errorStr
}
next('route')
}
return response
})
You're trying to do server side validation. In Node a good approach would be to define a JSON Schema for the expected parameters and then in your route handler validate the data sent in the request with a JSON Schema validator. This would help you work out whether a request was valid and help you generate error messages automatically. As a rule it's much better (i.e. simpler and more maintainable) to use tools that enable you to declaratively declare your validation (via a schema) than imperatively write code to manually validate objects.
JSON Schema spec https://json-schema.org/
A validator https://github.com/epoberezkin/ajv

Updating Stripe accounts returns "Error: Stripe: "id" must be a string, but got: object"

I am trying to update a Stripe account to add an external account token to be charged later as shown in the example here.
var stripe = require("stripe")("sk_test_xxxxxxxxxxxxx"),
knex = require("knex")(config);
router.post("/paymentcardinfo",middleware.isLoggedIn,function(req,res){
knex("users.stripe").select("stripe_id_key")
.then((stripeID) => {
stripeID = stripeID[0].stripe_id_key;
console.log("My Stripe ID: "stripeID);
console.log("stripeID var type:", typeof stripeID);
stripe.accounts.update({
stripeID,
external_account: req.body.stripeToken,
}, function(err,acct) {
if(err){
console.log(err);
} else {
console.log("SUCCESS ********",acct);
// asynchronously called
}})
})
.catch((e) => {
console.log(e);
res.redirect("/paymentcardinfo")
});
});
Which returns the following
My Stripe ID: acct_xxxxxxxxxxxxx
stripeID var type: string
[Error: Stripe: "id" must be a string, but got: object (on API request to `POST /accounts/{id}`)]
where acct_xxxxxxxxxxx is the user's stored account ID. Based on the first console.log value, it would appear that stripeID is a string and not an object, which makes me unsure of how to proceed with this error.
Although the documentation specifies
stripe.accounts.update({
{CONNECTED_STRIPE_ACCOUNT_ID},
metadata: {internal_id: 42},
}).then(function(acct) {
// asynchronously called
});`
The following worked for me
stripe.accounts.update(
CONNECTED_STRIPE_ACCOUNT_ID,
{
metadata: {internal_id:42},
}
).then((account) => {
// response to successful action

Meteor - How to find out if Meteor.user() can be used without raising an error?

I'm looking for a way to determine if Meteor.user() is set in a function that can be called both from the server and client side, without raising an error when it is not.
In my specific case I use Meteor server's startup function to create some dummy data if none is set. Furthermore I use the Collection2-package's autoValue -functions to create some default attributes based on the currently logged in user's profile, if they are available.
So I have this in server-only code:
Meteor.startup(function() {
if (Tags.find().fetch().length === 0) {
Tags.insert({name: "Default tag"});
}
});
And in Tags-collection's schema:
creatorName: {
type: String,
optional: true,
autoValue: function() {
if (Meteor.user() && Meteor.user().profile.name)
return Meteor.user().profile.name;
return undefined;
}
}
Now when starting the server, if no tags exist, an error is thrown: Meteor.userId can only be invoked in method calls. Use this.userId in publish functions.
So in other words calling Meteor.user() on the server startup throws an error instead of returning undefined or null or something. Is there a way to determine whether it will do so prior to calling it?
I cannot solve this simply by wrapping the call with if (Meteor.isServer) within the autoValue function, as the autoValue functions are normally called from server side even when invoked by the user, and in these cases everything in my code works fine.
Note that this is related to How to get Meteor.user() to return on the server side?, but that does not address checking if Meteor.user() is available in cases where calling it might or might not result in an error.
On the server, Meteor.users can only be invoked within the context of a method. So it makes sense that it won't work in Meteor.startup. The warning message is, unfortunately, not very helpful. You have two options:
try/catch
You can modify your autoValue to catch the error if it's called from the wrong context:
autoValue: function() {
try {
var name = Meteor.user().profile.name;
return name;
} catch (_error) {
return undefined;
}
}
I suppose this makes sense if undefined is an acceptable name in your dummy data.
Skip generating automatic values
Because you know this autoValue will always fail (and even if it didn't, it won't add a useful value), you could skip generating automatic values for those inserts. If you need a real name for the creator, you could pick a random value from your existing database (assuming you had already populated some users).
Been stuck with this for two days, this is what finally got mine working:
Solution: Use a server-side session to get the userId to prevent
"Meteor.userId can only be invoked in method calls. Use this.userId in publish functions."
error since using this.userId returns null.
lib/schemas/schema_doc.js
//automatically appended to other schemas to prevent repetition
Schemas.Doc = new SimpleSchema({
createdBy: {
type: String,
autoValue: function () {
var userId = '';
try {
userId = Meteor.userId();
} catch (error) {
if (is.existy(ServerSession.get('documentOwner'))) {
userId = ServerSession.get('documentOwner');
} else {
userId = 'undefined';
}
}
if (this.isInsert) {
return userId;
} else if (this.isUpsert) {
return {$setOnInsert: userId};
} else {
this.unset();
}
},
denyUpdate: true
},
// Force value to be current date (on server) upon insert
// and prevent updates thereafter.
createdAt: {
type: Date,
autoValue: function () {
if (this.isInsert) {
return new Date;
} else if (this.isUpsert) {
return {$setOnInsert: new Date};
} else {
this.unset();
}
},
denyUpdate: true
},
//other fields here...
});
server/methods.js
Meteor.methods({
createPlant: function () {
ServerSession.set('documentOwner', documentOwner);
var insertFieldOptions = {
'name' : name,
'type' : type
};
Plants.insert(insertFieldOptions);
},
//other methods here...
});
Note that I'm using the ff:
https://github.com/matteodem/meteor-server-session/ (for
ServerSession)
http://arasatasaygin.github.io/is.js/ (for is.existy)

Categories