I'm trying to change a remote method for the Idioma model which only had one parameter (id). This is is the actual functioning code:
Model.remoteMethod('idiomaByOferta', {
description: 'Obtener un idioma por oferta',
http: { path: '/oferta/:id', verb: 'get' },
accepts: [
{ arg: 'id', type: 'string', required: true },
{ arg: 'res', type: 'object', http: { source: 'res' } }
],
returns: {
type: 'Object',
root: true,
default: output_structure
}
});
Model.idiomaByOferta = (id, res, cb) => {
parameterValidatorId(id, err => {
if (err) {
res.status(httpStatus.BAD_REQUEST.code).send(err);
}
});
const conn = Model.app.datasources.db.connector;
commons
.getResultSqlString(conn, sqlEstablecimiento.findIdiomas, [id])
.then(stb => {
cb(null, stb);
})
.catch(err => cb(err, null));
};
Model.afterRemote('idiomaByOferta', async (ctx, result, next) => {
delete ctx.res.req.query.limit;
delete ctx.res.req.query.page;
delete query.limit;
delete query.page;
next();
});
Now I want to include another parameter but I haven't found exactly how to do it with required parameters. I have tried the following but it doesn't work:
Model.remoteMethod('idiomaByOferta', {
description: 'Obtener un idioma por oferta',
http: { path: '/oferta', verb: 'get' },
accepts: [
{ arg: 'id', type: 'string', required: true, http: { source: 'query' }},
{ arg: 'nivel', type: 'string', required: true, http: { source: 'query' }},
{ arg: 'res', type: 'object', http: { source: 'res' } }
],
returns: {
type: 'Object',
root: true,
default: output_structure
}
});
Request url: {{url}}/api/idiomas/oferta?id={{oferta}}&nivel=Inicial
Response:
{
"errors": [
{
"code": 938,
"source": "id",
"detail": "is not allowed"
},
{
"code": 963,
"source": "nivel",
"detail": "is not allowed"
}
]
}
I have also tried doing this:
Model.remoteMethod('idiomaByOferta', {
description: 'Obtener un idioma por oferta',
http: { path: '/oferta/:id/nivel/:nivel', verb: 'get' },
accepts: [
{ arg: 'id', type: 'string', required: true},
{ arg: 'nivel', type: 'string', required: true},
{ arg: 'res', type: 'object', http: { source: 'res' } }
],
returns: {
type: 'Object',
root: true,
default: output_structure
}
});
The request times out and is never completed.
The position of your accepts attributes is important.
In your http attribute, the argument path is optionnal and is useful if you want to change the order of your accepts attributes or simply modify the path name.
What I would do is:
Model.remoteMethod('idiomaByOferta', {
description: 'Obtener un idioma por oferta',
http: { path: '/oferta/:id/:nivel', verb: 'get' },
accepts: [
{ arg: 'id', type: 'string', required: true },
{ arg: 'nivel', type: 'string', required: true},
{ arg: 'res', type: 'object', http: { source: 'res' } }
],
returns: {
type: 'Object',
root: true,
default: output_structure
}
});
Model.idiomaByOferta = (id, nivel, res, cb) => { //add nivel here in second position
parameterValidatorId(id, err => {
if (err) {
res.status(httpStatus.BAD_REQUEST.code).send(err);
}
});
parameterValidatorId(nivel, err => {
if (err) {
res.status(httpStatus.BAD_REQUEST.code).send(err);
}
});
const conn = Model.app.datasources.db.connector;
commons
.getResultSqlString(conn, sqlEstablecimiento.findIdiomas, [id, nivel]) //and use it there, maybe, depending on what your code is doing?
.then(stb => {
cb(null, stb);
})
.catch(err => cb(err, null));
};
Related
I am trying to add in validation on my MongoDB server and everything I give it fails document validation.
The most common error I keep getting:
details: { operatorName: '$and', clausesNotSatisfied: [Array] }
The validation logic I'm using:
{
title: 'Facebook Conversation',
type: 'object',
properties: {
participants: {
type: 'array',
items: {
type: 'object',
properties: {
name: {
type: 'string'
}
},
required: [
'name'
],
additionalProperties: true
}
},
messages: {
type: 'array',
items: {
type: 'object',
properties: {
sender_name: {
type: 'string'
},
timestamp_ms: {
type: 'number'
},
content: {
type: 'string'
},
type: {
type: 'string'
},
photos: {
type: 'array',
items: {
type: 'object',
properties: {
uri: {
type: 'string'
},
creation_timestamp: {
type: 'number'
}
},
required: [
'uri',
'creation_timestamp'
]
}
},
ip: {
type: 'string'
},
sticker: {
type: 'object',
properties: {
uri: {
type: 'string'
}
}
},
payment_info: {
type: 'object',
properties: {
amount: {
type: 'number'
},
currency: {
type: 'string'
},
creationTime: {
type: 'number'
},
completedTime: {
type: 'number'
},
senderName: {
type: 'string'
},
receiverName: {
type: 'string'
}
}
},
call_duration: {
type: 'number'
},
missed: {
type: 'boolean'
},
gifs: {
type: 'array',
items: {
type: 'object',
properties: {
uri: {
type: 'string'
}
},
required: [
'uri'
]
}
},
share: {
type: 'object',
properties: {
link: {
type: 'string'
},
share_text: {
type: 'string'
}
}
},
videos: {
type: 'array',
items: {
type: 'object',
properties: {
uri: {
type: 'string'
},
creation_timestamp: {
type: 'number'
},
thumbnail: {
type: 'object',
properties: {
uri: {
type: 'string'
}
}
}
},
required: [
'uri',
'creation_timestamp',
'thumbnail'
]
}
},
reactions: {
type: 'array',
items: {
type: 'object',
properties: {
reaction: {
type: 'string'
},
actor: {
type: 'string'
}
},
required: [
'reaction',
'actor'
]
}
},
audio_files: {
type: 'array',
items: {
type: 'object',
properties: {
uri: {
type: 'string'
},
creation_timestamp: {
type: 'number'
}
},
required: [
'uri',
'creation_timestamp'
]
}
},
users: {
type: 'array',
items: {
type: 'object',
properties: {
name: {
type: 'string'
}
},
required: [
'name'
]
}
},
files: {
type: 'array',
items: {
type: 'object',
properties: {
uri: {
type: 'string'
},
creation_timestamp: {
type: 'number'
}
}
}
}
},
required: [
'sender_name',
'timestamp_ms',
'content'
]
}
},
title: {
type: 'string'
},
is_still_participant: {
type: 'boolean'
},
thread_type: {
type: 'string'
},
thread_path: {
type: 'string'
}
}
}
An example of a JSON file that fails document validation:
{
"participants": [
{
"name": "Person"
},
{
"name": "Me"
}
],
"messages": [
{
"sender_name": "Person",
"timestamp_ms": 1550000006885,
"content": "Message content",
"type": "Generic"
}
],
"title": "Person",
"is_still_participant": true,
"thread_type": "Regular",
"thread_path": "inbox/Person"
}
Typescript code I'm using:
apiRouter.post("/", async (req: any, res: any) => {
res.setHeader("Content-Type", "application/json; charset=utf-8");
try {
let newConversation = JSON.stringify(req.body);
const result = await collections.conversations?.insertOne(newConversation);
result
? res.status(201).send(`Successfully created a new conversation`)
: res.status(500).send("Failed to create a new conversation.");
} catch (error) {
if (error instanceof Error) {
console.error(error);
res.status(400).send(error.message);
} else {
console.log('Unexpected error', error);
}
}
});
If I use JSON.stringify on the req.body, I get this error:
Cannot create property '_id' on string
So I'm really confused as to what I'm doing wrong, I actually allowed JSON to pass without validation and then exported the schema in Compass and used that as validation logic and that doesn't work as well. But maybe that's not meant to be used as validation logic and that's why. But if anybody could help me figure this out, I'd appreciate it.
I figured it out, when I added the JSON as validation in MongoDB, I used Compass and it didn't accept the $schema tag and when I removed it, it allowed me to apply it, so I assumed I entered everything correctly.
However I was looking around MongoDB's website to see examples of JSON schemas and I noticed they enclosed it with $jsonSchema: {}, they were using the shell so I assumed that's why. But I decided to try it and finally documents are successfully validating!
I have a fastify route method with the following schema.
fastify.post('/club', createClubSchema, createClub(fastify));
const createClubSchema = {
schema: {
tags: ['club'],
security: [
{
ApiKeyAuth: [],
},
],
body: {
type: 'object',
required: ['name', 'description'],
properties: {
name: { type: 'string', minLength: 3 },
description: { type: 'string', minLength: 3 },
logoUrl: { type: 'string', minLength: 3 },
},
},
response: {
200: {
type: 'object',
properties: {
status: { type: 'string' },
data: {
type: 'object',
properties: {
name: { type: 'string' },
description: { type: 'string' },
id: { type: 'number' },
color: { type: 'string' },
logoUrl: { type: 'string' },
createdAt: { type: 'string' },
updatedAt: { type: 'string' },
},
},
},
},
},
},
preHandler: [grantAccess('create', 'club')],
};
Now this route is in folder where there is autohooks.js is located that has a prehandler hook of it's own which checks whether the request has token in it for authentication purposes.
The problem is after the preHandler hook is called on both the area the createClub controller method is called, so a total of 2 times.
What is the issue? and how can i solve this?
EDIT
This is the autoHooks.js file
const authentication = require('../../middlewares/authentication');
module.exports = async function (fastify, opts, next) {
fastify.addHook('preHandler', authentication.authenticate(fastify));
};
I am trying to get data mapped with empid from 2 tables viz-skillsrepo and certifications and render it to frontend,I am getting all data from certifications table,but i need data only of the empid which i send in request
tried using includes method
app.get('/updateprofile/:id', function (req, res) {
db.skillsrepo.find({
where: { employeeId: req.params.id },
include: [
{
model: db.certifications
},
{
model: db.attachments
},
{
model: db.project
}
]
}).then(result => {
if (result != null) {
res.render('updateprofile', {
user: result,
eid: req.params.id,
});
console.log("**********", result)
}
})
});
This is the Schema:
var skillsrepo = exports.skillsrepo = connection.define('skillsrepo', {
firstname: {
type: Sequelize.STRING(23)
},
lastname: {
type: Sequelize.STRING(23)
},
highQual: {
type: Sequelize.STRING(23)
},
fivekeystrenghts: {
type: Sequelize.TEXT
},
domain: {
type: Sequelize.STRING(23)
},
technicalskills: {
type: Sequelize.STRING(23)
},
typesoftesting: {
type: Sequelize.STRING(23)
},
employeeId: {
type: Sequelize.INTEGER(11),
references: {
model: 'employeemastertablee',
key: 'id'
}
}
});
skillsrepo.hasMany(certifications, {
foreignKey: "employeeId"
});
certifications.belongsTo(skillsrepo, {
foreignKey: "employeeId"
});
I'm looking for a way to prevent unwanted properties to be present in the requestBody as described in the associated Model
Here is my model :
import { Model, model, property } from '#loopback/repository';
#model({
name: 'AwsS3',
strict: true,
description: 'AWS S3 Object description',
properties: {
Key: {
type: 'String',
required: 'true',
},
Bucket: {
type: 'String',
requied: 'true',
},
},
})
export class AwsS3 extends Model {
#property({
type: 'string',
description: 'path/to/file',
required: true,
}) Key: string;
#property({
type: 'string',
description: 'AWS-S3-Bucket-Name',
required: true,
})
Bucket: string;
constructor(data: AwsS3) {
super(data);
}
}
I used it like this in the controller
function(#requestBody({
required: true,
description: 'aws object settings',
content: {
'application/json': {},
},
}) body : AwsS3
){
console.log(body);
}
It throws correctly when one of both properties is missing or in the wrong type.
But if i send a json like bellow nothing is thrown and object is processed with the UnwantedProp
{
Key: 'key',
Bucket : 'bucket',
UnwantedProp: 40
}
I found it to be achievable by using the #api decorator and setting the additionalProperties: false from the openapi specs.
use it like :
#api(
basePath: '/',
paths : {
'somepath': {
'post' : {
'x-operation-name': 'myfunction',
'x-controller-name': 'MyController',
// properties for route
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
additionalProperties: false, // <=== here it is
properties: {
Key: { type: 'string'},
Bucket: {type: 'string'},
},
required: ['Bucket', 'Key'],
},
},
},
},
}
}
}
)
export class MyController{
async myfunction(
#requestBody({ settings: { strict: true } }) body
){}
}
when testing it throws the following as expected :
{
"error": {
"statusCode": 422,
"name": "UnprocessableEntityError",
"message": "The request body is invalid. See error object `details` property for more info.",
"code": "VALIDATION_FAILED",
"details": [
{
"path": "",
"code": "additionalProperties",
"message": "should NOT have additional properties",
"info": {
"additionalProperty": "unwantedProp"
}
}
]
}
}
Maybe my question is very simple and superficial
But please, guide me
my Place Model is:
module.exports = {
attributes: {
title: {
type: 'string',
},
body: {
type: 'string',
columnType: 'text',
},
address: {
type: 'string',
columnType: 'text',
},
x_map: {
type: 'number',
columnType: 'float'
},
y_map: {
type: 'number',
columnType: 'float'
},
like: {
type: 'number',
columnType: 'integer'
},
dis_like: {
type: 'number',
columnType: 'integer'
},
visited: {
type: 'number',
columnType: 'integer'
},
emtiaz: {
type: 'number',
columnType: 'integer'
},
tags: {
type: 'string',
},
city:{
collection: 'city',
via: 'place_owner'
},
},
};
And my City Model is:
module.exports = {
attributes: {
title: {
type: 'string',
},
ostan_owner :{
model: 'ostan'
},
place_owner: {
model: 'place'
}
},
};
And my place controller is:
create: function (req, res, next) {
Place.create(req.params.all, function place_created(err,new_place){
if(err && err.invalidAttributes) {
err = validator(Place, err);
return res.json({'status':false, 'errors':err.Errors});
}
else{
new_place.add('city',req.param('city'));
new_place.save(function (err) {
if(err){
return res.json({'status':false,'errors':err});
}
else {
res.json({'status':true,'result':new_place});
}
});
}
});
},
now whene i try to create new place and add new city to collection it give me Error:
TypeError: Cannot read property 'add' of undefined
at place_created (C:\Programing_workspace\gardeshgar_sailsV1\gardeshgar\api\controllers\PlaceController.js:28:19)
whene i use model_X.addToCollection i recive same error
i use sails v 1.0 and i am new in sails.js
please Help me
Sails.js 1.0 does not support .add() and .save() anymore.
Please use .addToCollection() instead.
await Place.addToCollection(new_place.id, 'city').members(req.param('city'));
The other problem is that sails.js 1.0 does not automatically fetch the created/updated Object (new_place in your case).
You need to add { fetch: true } in order the get the created object.
For example:
User.create({...}, function(err, createdUser) {
//...
}, { fetch: true });
Or using await:
var createdUser = await User.create({...}).fetch();
I think, in your case, the solution should be:
create: function (req, res, next) {
Place.create(req.params.all, function place_created(err,new_place){
if(err && err.invalidAttributes) {
err = validator(Place, err);
return res.json({'status':false, 'errors':err.Errors});
}
else{
await Place.addToCollection(new_place.id, 'city').members(req.param('city')).exec(function (err) {
if(err){
return res.json({'status':false,'errors':err});
}
else {
res.json({'status':true,'result':new_place});
}
});
}
}, { fetch: true });
},