how can i use ajv-i18n in fastify ? - ajv

Background
fastify
json schema
ajv
Problem
when i add the setErrorHandler to my project/index.js,it doesnt work.
require('module-alias/register')
const Fastify = require('fastify')
const PORT = process.env.PORT || 3000
const sequelize = require('./orm')
const swagger = require('./config').swagger
const localize = require('ajv-i18n')
const app = Fastify({
logger: {
prettyPrint: true
},
ajv: {
customOptions: {allErrors: true, jsonPointers: true },
plugins: [
require('ajv-errors')
]
}
})
app.register(require('fastify-sensible'))
app.register(require('fastify-swagger'), swagger)
app.register(require('./plugin/systemlogs'))
app.register(require('./plugin/authenticate')).then(()=>{
const routes = require('./routes')
routes(app).forEach((route, index) => {
app.route(route)
})
})
app.setErrorHandler((error,request,reply)=>{
if (error.validation) {
localize.ru(error.validation)
reply.status(400).send(error.validation)
return
}
reply.send(error)
})
const start = async () => {
try {
await sequelize.sync({})
app.log.info('database sync correctly')
await app.listen(PORT, '0.0.0.0')
app.swagger()
} catch (err) {
app.log.error(err)
process.exit(1)
}
}
start()
Question
i want to turn the error to chinese with ajv i18n ,what should i do? i do in this way but it doesnt work
how can i use ajv-i18n in fastify ?
where should i add the setErrorHandler?

there are another way to deal with this issue.
thanks:
L2jLiga https://github.com/L2jLiga
Manuel Spigolon
url:
https://github.com/fastify/help/issues/317
this is another way:
const fastify = require('fastify')
const localize = require('ajv-i18n')
const app = fastify({
ajv: {
customOptions: { allErrors: true, jsonPointers: true },
plugins: [
require('ajv-errors')
]
},
schemaErrorFormatter: (errors, dataVar) => {
localize.ru(errors);
const myErrorMessage = errors.map(error => error.message.trim()).join(', ');
return new Error(myErrorMessage)
}
})
app.get('/', {
schema: {
querystring: {
type: "object",
properties: { a: { type: "string", nullable: false } },
required: ["a"],
},
},
}, async function (req, res) {})
app.listen(3000)
and example of response:
{
"statusCode": 400,
"error": "Bad Request",
"message": "должно иметь обязательное поле a"
}

Here a working snippet to play with; I think your issue is on the routes' schemas.
const Fastify = require('fastify')
const localize = require('ajv-i18n')
const app = Fastify({
logger: true,
ajv: {
customOptions: { allErrors: true, jsonPointers: true }
}
})
app.post('/', {
schema: {
body: {
type: 'object',
properties: {
foo: { type: 'integer' }
}
}
}
}, () => {})
app.setErrorHandler((error, request, reply) => {
if (error.validation) {
localize.ru(error.validation)
reply.status(400).send(error.validation)
return
}
request.log.error({ err: error })
reply.send(error)
})
app.inject({
method: 'POST',
url: '/',
payload: {
foo: 'string'
}
}, (_, res) => {
console.log(res.json())
})
That will print out:
[
{
keyword: 'type',
dataPath: '/foo',
schemaPath: '#/properties/foo/type',
params: { type: 'integer' },
message: 'должно быть integer'
}
]

Related

Error in Postman 500 Internal server error post method

It's my first time using Next Js, Mongo DB and Postman. I am building an app and when sending the information I get a 500 error in postman.
In VSC all is ok and the console does not have any errors
Can you guide me with some ideas to fix it?
I hope that the information is sent correctly and that it gives me a 201 code. As in the screenshot of the tutorial
Link to tutorial:https://www.youtube.com/watch?v=Z-hACIsjv4E&t=3115s
I'am using mongoose to...
import mongoose from "mongoose";
const MONGO_URL = process.env.MONGO_URL;
console.log(process.env.MONGO_URL);
if (!MONGO_URL) {
throw new Error(
"Please define the MONGO_URL environment variable inside .env.local"
);
}
let cached = global.mongoose;
if (!cached) {
cached = global.mongoose = { conn: null, promise: null };
}
async function dbConnect() {
if (cached.conn) {
return cached.conn;
}
if (!cached.promise) {
const opts = {
bufferCommands: false,
};
cached.promise = mongoose.connect(MONGO_URL, opts).then((mongoose) => {
return mongoose;
});
}
cached.conn = await cached.promise;
return cached.conn;
}
export default dbConnect;
import dbConnect from "../../../util/mongo";
import Product from "../../../models/Product";
export default async function handler(req, res) {
const { method } = req;
dbConnect();
if (method === "GET") {
try {
} catch (err) {}
}
if (method === "POST") {
try {
const product = await Product.create(req.body);
req.status(201).json(product);
} catch (err) {
res.status(500).json(err);
}
}
}
import mongoose from "mongoose";
const OrderSchema = new mongoose.Schema(
{
customer: {
type: String,
required: true,
maxlength: 60,
},
address: {
type: String,
required: true,
maxlength: 200,
},
total: {
type: Number,
required: true,
},
status: {
type: Number,
default: 0,
},
method: {
type: Number,
required: true,
},
},
{ timestamps: true }
);
export default mongoose.models.Order || mongoose.model("Order", OrderSchema);

Accessing the values from Promise inside useEffect hook

const useOnfidoFetch = (URL) => {
useEffect(() => {
const appToken = axios.get('http://localhost:5000/post_stuff')
.then((response) => response.data.data.data.json_data)
.then((json_data) => {
const id = json_data.applicant_id;
const token = json_data.onfido_sdk_token;
return {id, token};
});
if (appToken) {
console.log('this is working!');
OnfidoSDK.init({
// the JWT token you generated above
token: null,
containerId: "root",
steps: [
{
type: 'welcome',
options: {
title: 'Open your new bank account',
},
},
'document'
],
onComplete: function (data) {
console.log('everything is complete');
axios.post('https://third/party/api/v2/server-api/anonymous_invoke?aid=onfido_webapp', {
params: {
applicant_id: appToken.applicant_id
}
});
}
});
}
}, [URL]);
}
export default function() {
const URL = `${transmitAPI}/anonymous_invoke?aid=onfido_webapp`;
const result = useOnfidoFetch(URL, {});
return (
<div id={onfidoContainerId} />
);
}
I have refactored this dozens of times already, I am getting back some values from the appToken Promise, but I need to provide the token value from that Promise to that token property inside of Onfido.init({}) and I need to provide the id to the applicant_id property and I continue to get undefined.
If you need the token for something else as well, then i would suggest storing it in useState, and triggering OnfidoSDK.init when the value of that state changes.
Like this:
const useOnfidoFetch = (URL) => {
const [token, setToken] = useState();
useEffect(() => {
axios.get('http://localhost:5000/post_stuff')
.then((response) => response.data.data.data.json_data)
.then((json_data) => {
const token = json_data.onfido_sdk_token;
setToken(token);
})
}, [URL])
useEffect(() => {
if (!token) return;
OnfidoSDK.init({
// the JWT token you generated above
token,
containerId: "root",
steps: [
{
type: 'welcome',
options: {
title: 'Open your new bank account',
},
},
'document'
],
onComplete: function (data) {
console.log('everything is complete');
axios.post('https://third/party/api/v2/server-api/anonymous_invoke?aid=onfido_webapp', {
params: {
applicant_id: appToken.applicant_id
}
});
}
});
}, [token]);
}
Move the entire if(appToken){ ... } inside the body of the second .then((json_data) => { ... })
Something like this:
const useOnfidoFetch = (URL) => {
useEffect(() => {
const appToken = axios.get('http://localhost:5000/post_stuff')
.then((response) => response.data.data.data.json_data)
.then((json_data) => {
const id = json_data.applicant_id;
const token = json_data.onfido_sdk_token;
// Here. This code will be executed after the values are available for id and token
if (appToken) {
console.log('this is working!');
OnfidoSDK.init({
// the JWT token you generated above
token: null,
containerId: "root",
steps: [
{
type: 'welcome',
options: {
title: 'Open your new bank account',
},
},
'document'
],
onComplete: function (data) {
console.log('everything is complete');
axios.post('https://third/party/api/v2/server-api/anonymous_invoke?aid=onfido_webapp', {
params: {
applicant_id: appToken.applicant_id
}
});
}
});
}
return {id, token};
});
// In here the promise is not yet finished executing, so `id` and `token` are not yet available
}, [URL]);
};
export default function() {
const URL = `${transmitAPI}/anonymous_invoke?aid=onfido_webapp`;
const result = useOnfidoFetch(URL, {});
return (
<div id={onfidoContainerId} />
);
}
For better readability, you could also move the if(appToken){ ... } block inside a separate function that takes id, token as arguments, which you can call from inside the promise.then block.

Update query timing out after 10 seconds despite successfully submitting in React app

I have the following Schema:
const SubmitDebtSchema = new Schema ({
balance: [{
balanceDate: Date,
newBalance: Number
}]
});
And I have the following function which appends new objects into the array:
module.exports = async (req, res) => {
let newDateObject = {
balanceDate: req.body.balanceDate,
newBalance: req.body.newBalance
};
await SubmitDebt.findOneAndUpdate(
{ _id: req.query.id },
{ $push: { balance: newDateObject } },
{new: true, useFindAndModify: false}
);
};
However, despite the database successfully updating, it times out after 10 seconds with the following error message:
2020-11-23T16:51:57.138Z 25fa69c2-91a1-4a74-8034-af132d4d8eb3 Task
timed out after 10.01 seconds
Does anyone have any feedback for how to resolve this? It also doesn't push to my 'dashboard' upon successful submission.
Here's my Axios front-end call:
onSubmit = async (e) => {
e.preventDefault();
let newBalanceDate = new Date();
this.calculateUpdatedBalance()
await axios.post("/api/edit/editDebtBalance",
{
balanceDate: newBalanceDate,
newBalance: this.calculateUpdatedBalance(),
},
{
params: {
id: this.props.match.params.id
}
}
)
this.props.history.push('/dashboard');
}
EDIT: My full serverless function file:
const express = require("express");
const mongoose = require("mongoose");
const bodyParser = require("body-parser");
const SubmitDebt = require("../submit/submitDebtSchema");
require("dotenv").config();
const app = express();
app.use(bodyParser.urlencoded({
extended: true
}));
mongoose.connect(process.env.MONGO_URI);
module.exports = async (req, res) => {
let newDateObject = {
balanceDate: req.body.balanceDate,
newBalance: req.body.newBalance
};
return await SubmitDebt.findOneAndUpdate(
{ _id: req.query.id },
{ $push: { balance: newDateObject } },
{new: true, useFindAndModify: false}
);
};
How about you try returning the value?
A lambda function stops only when you return something, try this.
return await SubmitDebt.findOneAndUpdate(
{ _id: req.query.id },
{ $push: { balance: newDateObject } },
{new: true, useFindAndModify: false}
);
Notice that I only added return before the await statement, that's it.
Edit: Ahh.... I think this should work
let response = await SubmitDebt.findOneAndUpdate(
{ _id: req.query.id },
{ $push: { balance: newDateObject } },
{new: true, useFindAndModify: false}
);
return res.send(response)
since you're using an express app. I'm sorry I overlooked it previously.

Need help saving a game to a user's favorited games

I'm receiving an error when trying to associate a saved game to the user that saves it. The error says "cannot read property push of undefined"
The user, and game can be read in the console. I think it may have something to do with the user model during the initial creation of the user, however I can't be sure. I did notice if I try to console.log(user.favGames) it will be returned undefined.
I've tried everything I can think of, I've re-written the controller roughly 10 times, to no avail.
user model
const mongoose = require('mongoose')
const bcrypt = require('bcrypt')
const SALT_ROUNDS = 6
const Schema = mongoose.Schema
const userSchema = new Schema(
{
username: { type: String, unique: true },
email: { type: String, unique: true, unique: true },
password: { type: String, required: true },
avatar: { type: String },
favGames: { type: Schema.Types.ObjectId, ref: 'Game', default: null },
comments: { type: Schema.Types.ObjectId, ref: 'Comment', default: null }
},
{
timestamps: true
}
)
userSchema.set('toJSON', {
transform: function(doc, ret) {
delete ret.password
return ret
}
})
userSchema.pre('save', function(next) {
const user = this
if (!user.isModified('password')) return next()
bcrypt.hash(user.password, SALT_ROUNDS, function(err, hash) {
if (err) return next()
user.password = hash
next()
})
})
userSchema.methods.comparePassword = function(tryPassword, cb) {
bcrypt.compare(tryPassword, this.password, cb)
}
module.exports = mongoose.model('User', userSchema)
game model
const mongoose = require('mongoose')
const Schema = mongoose.Schema
let gameSchema = new Schema({
name: { type: String, required: true },
boxArtUrl: { type: String, required: true },
twitchID: { type: String, required: true },
comments: { type: Schema.Types.ObjectId, ref: "Comment"}
})
module.exports = mongoose.model('Game', gameSchema)
game router
const express = require('express')
const router = express.Router()
const gamesCtrl = require('../../controllers/gameCtrl')
function isAuthed(req, res, next) {
if (req.user) return next()
return res.status(401).json({ msg: 'Unauthorized ' })
}
router.get('/')
router.post('/', isAuthed, gamesCtrl.addGame)
module.exports = router
game controller
const User = require('../models/user')
const Game = require('../models/Game')
function addGame(req, res) {
Game.create({
name: req.body.name,
twitchID: req.body.id,
boxArtUrl: req.body.box_art_url
})
.then(game => {
User.findById(req.user._id)
.then(user => {
console.log(game)
console.log(user.favGames)
// user.favGames.push(game)
// user.save()
})
.catch(err =>
console.log('error when updating user with new game', err)
)
})
.catch(err => console.log('error saving game', err))
}
module.exports = {
addGame
}
the error is flagged in my controller at user.favGames.push(game). Note that when a user creates a profile there are no games associated with their profile. I'm pretty sure I'm calling on the actual data instance of the model, not the model itself. Thanks in advance for your assistance.
Your favGames (and also comments) must be defined as array in user model like this.
const userSchema = new Schema(
{
username: { type: String, unique: true },
email: { type: String, unique: true, unique: true },
password: { type: String, required: true },
avatar: { type: String },
favGames: [{ type: Schema.Types.ObjectId, ref: 'Game', default: null }],
comments: [{ type: Schema.Types.ObjectId, ref: 'Comment', default: null }]
},
{
timestamps: true
}
)
Also user.save() returns a promise, so you need use then block, or await.
So the addGame function must be like this (I converted the code to async/await)
async function addGame(req, res) {
try {
let game = await Game.create({
name: req.body.name,
twitchID: req.body.id,
boxArtUrl: req.body.box_art_url
});
let user = await User.findById(req.user._id);
if (user) {
user.favGames.push(game);
await user.save();
res.status(200).send("game and user saved");
} else {
console.log("user not found");
res.status(404).send("user not found");
}
} catch (err) {
console.log("Err: ", err);
res.status(500).send("Something went wrong");
}
}
Looks like it's a matter of checking to see if it exists:
User.findById(req.user._id)
.then(user => {
if (!Array.isArray(user.favGames)) {
user.favGames = [];
}
user.favGames.push(game);
user.save();
})

TypeError: http is not a function in Jest framework

I am using React.js as my JavaScript framework, where I have installed Jest and using pact (CDC) for unit test cases and while I run the command npm run test the spec.js file is failing and throwing an error like this
TypeError: http is not a function
This is criteria-managementcomponent.spec.js file
const path = require('path')
const pact = require('pact')
const API_PORT = process.env.API_PORT || 9123
const {
fetchProviderData, getCriteriaManagementComponent, criteriaManagementComponent
} = require('../client')
// Configure and import consumer API
// Note that we update the API endpoint to point at the Mock Service
const LOG_LEVEL = process.env.LOG_LEVEL || 'WARN'
const provider = pact({
consumer: 'Web Login',
provider: 'Web API',
port: API_PORT,
log: path.resolve(process.cwd(), 'logs', 'pact.log'),
dir: path.resolve(process.cwd(), 'pacts'),
logLevel: LOG_LEVEL,
spec: 2
})
describe('Started Testing Pact', () => {
beforeEach((done) => {
return provider.setup().then(() => done());
});
afterEach((done) => {
return provider.finalize().then(() => done())
})
describe('criteriaManagement', () => {
beforeEach(() => {
let criteriaManagement = {
uponReceiving: 'wrong criteriaManagement',
state: 'Code OK',
withRequest: {
method: 'GET',
path: '/api/criteria',
},
willRespondWith: {
status: 200,
headers: {
'Content-Type': 'application/json; charset=utf-8'
},
body: {
code: "string",
context: {},
message: "string",
payload: [
{
country: "string",
createdBy: "string",
displayOrder: 0,
id: "string",
lastModifiedBy: "string",
name: "string",
translations: {},
warehouse: "string"
}
]
}
}
};
return provider.addInteraction(criteriaManagement)
})
afterEach(() => {
return provider.verify()
})
it('Login test', done => {
criteriaManagementComponent().then((res) => {
expect(res.code).not.toBeNull();
expect(res.context).toBeDefined();
expect(res.message).not.toBeNull();
expect(res.payload.country).not.toBeNull();
expect(res.payload.createdBy).not.toBeNull();
expect(res.payload.displayOrder).toBe(0);
expect(res.payload.id).not.toBeNull();
expect(res.payload.lastModifiedBy).not.toBeNull();
expect(res.payload.translations).toBeDefined();
expect(res.payload.name).not.toBeNull();
expect(res.payload.warehouse).not.toBeNull();
});
done();
})
});
})
This is client.js file for criteria-managementcomponent.spec.js file
const request = require('superagent')
const API_HOST = process.env.API_HOST || 'http://localhost'
const API_PORT = process.env.API_PORT || 9123
const API_ENDPOINT = `${API_HOST}:${API_PORT}`
// Fetch provider data
const fetchProviderData = (submissionDate) => {
return request
.get(`${API_ENDPOINT}/provider`)
.query({ validDate: submissionDate })
.then((res) => {
console.log("Response :: ", res.body);
return {
count: 100 / res.body.count,
date: res.body.date
}
})
}
const getCriteriaManagementComponent = (accessToken, expiresIn) => {
return request
.post(`${API_ENDPOINT}/api/criteria`)
.then((res) => {
return {
code : res.body.code
}
})
}
const criteriaManagementComponent = () => {
// const request = require('request')
console.log("End Point =========> ", `${API_ENDPOINT}/api/criteria`)
return request.get(`${API_ENDPOINT}/api/criteria`).then((res) => {
return {
code : res.body.code,
context:res.body.context,
message:res.body.message,
payload:res.body.payload,
profile:res.body.payload.profile,
successful : res.body.successful
}
})
};
module.exports = {
fetchProviderData, getCriteriaManagementComponent, criteriaManagementComponent
}
It's hard to know where things are going wrong without a line number in the stack trace, however one thing is for certain - you're using a very old version of pact.
You should be using #pact-foundation/pact now.
As for the http issue, is it possible you have any mocks setup that are interfering? The issue seems unrelated to pact.
Lastly, if you could provide a reproducible github repo that others could download and run, you could probably get even better help.

Resources