formData Joi validation is failed when we use nested object - node.js

Issue
I am working on file upload operation on new hapiJS framework with the latest hapi-swagger version, The older one is supported correctly but now I facing some problem while I am using hapi-swagger payload type form.
Is there something I missed?
Used libraries
hapi-hapi 19.1.1
hapi-Joi: 17.1.1
Hapi-swagger: 13.0.2
Node: 12.18.1
const Joi = require("#hapi/joi");
// Route file
app.route({
method: 'POST',
path: '/fileUpload/{format}',
options: Controller.fileUpload()
});
// Controller file
public fileUpload(): Hapi.RouteOptions{
return {
handler: async (request: any, h: Hapi.ResponseToolkit) => {
try {
let file = request.payload.file
let data = request.payload.data
let fileFormat = request.params.format
//some logic
} catch (err) {
throw error;
}
},
tags: ['api', 'fileUpload', 'POST'],
description: 'file uploading',
plugins: {
'hapi-swagger': {
payloadType: 'form'
}
},
validate: {
params: Joi.object({
format: Joi.string().required().description('Format of file').example('docx, xml or html').allow('docx', 'xml', 'html')
}),
payload: Joi.object({
data: Joi.object({
objectData: Joi.object({
objectName: Joi.string().required().description('Name of object').example('xyz').disallow('null', 'undefined'),
objectRID: Joi.string().optional().example('#24:6').disallow('null', 'undefined')
}).required(),
objectTravelRelations: Joi.array().optional().items(
Joi.object({
objectName: Joi.string().required().description('Class of Object').disallow('null', 'undefined'),
objectUID: Joi.string().required().example('873cc6cd-d51d-44d0-8377-2061641a11a3').disallow('null', 'undefined'),
relation: Joi.string().required().description('Name of the relation').example('HasChild').disallow('null', 'undefined'),
direction: Joi.string().required().description('Direction of relation').example('in').allow('in', 'out').disallow('null', 'undefined')
}).required()
)
}),
file: Joi.any()
.meta({ swaggerType: 'file' }).required()
.description('file.')
}),
options: {
allowUnknown: true
},
failAction: async (request, h, error) => {
throw error;
},
},
payload: {
output: 'stream',
parse: true,
multipart: true,
maxBytes: 10485760
}
};
}
Expected Behavior
The form data is validated and the file upload successfully.
Actual Behavior
data has treated a string but actually its an object
message: ' "data" must be of type object '

Related

ERROR: Module not found: Can't resolve 'dns'

I am building an application in React. My problem is that if I navigate to the route '/factura/api/invoices/${invoiceId}' and press the edit button, it should send the information to the MongoDB database and return that everything is fine, but when I do it, I get the following error.
./node_modules/mongodb/lib/cmap/auth/gssapi.js:4:0
Module not found: Can't resolve 'dns'
Import trace for requested module:
./node_modules/mongodb/lib/index.js
./pages/facturas/api/edit/[invoiceId]/index.js
https://nextjs.org/docs/messages/module-not-found
I am not sure why I am receiving this error. This is my first part of the code located in the '/facturas/edit/[invoiceId]' folder:
const updateInvoice = async (invoiceId, status) => {
try {
const res = await fetch(`/facturas/api/edit/${invoiceId}`, {
method: 'PUT',
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
senderStreet: senderStreet,
senderCity: senderCity,
senderPostalCode: senderPostalCode,
senderCountry: senderCountry,
clientName: clientName,
clientEmail: clientEmail,
clientStreet: clientStreet,
clientCity: clientCity,
clientPostalCode: clientPostalCode,
clientCountry: clientCountry,
description: description,
createdAt: createdAt,
paymentDue: createdAt,
paymentTerms: paymentTerms,
status: status,
items: items,
total: totalAmount
})
})
const data = await res.json()
router.push(`/facturas/invoices/${invoiceId}`)
toast.success(data.message)
} catch (error) {
toast.error("Something is wrong")
console.log(error)
}
}
And this is the second part of the code in the file located in the API folder:
import { MongoClient, ObjectId } from "mongodb";
async function handler(req, res) {
const { invoiceId } = req.query;
const client = await MongoClient.connect('mongodb+srv://test:test#cluster0.uusfifl.mongodb.net/invoices?retryWrites=true&w=majority', { useNewUrlParser: true });
const db = client.db();
const collection = db.collection("allInvoices");
if (req.method === "PUT") {
await collection.updateOne(
{
_id: ObjectId(invoiceId),
},
{
$set: {
senderAddress: {
street: req.body.senderStreet,
city: req.body.senderCity,
postalCode: req.body.senderPostalCode,
country: req.body.senderCountry,
},
clientName: req.body.clientName,
clientEmail: req.body.clientEmail,
clientAddress: {
street: req.body.clientStreet,
city: req.body.clientCity,
postalCode: req.body.clientPostalCode,
country: req.body.clientCountry,
},
createdAt: req.body.createdAt,
paymentDue: req.body.createdAt,
paymentTerms: req.body.paymentTerms,
description: req.body.description,
status: req.body.status,
items: req.body.items,
total: req.body.total,
},
}
);
res.status(200).json({ message: "Invoice updated successfully" });
}
client.close();
}
export default handler;
And here are all the files that I have used, in case you want to see them:
https://github.com/Fukene/tarea_database
Thanks.
I tried to reinstall MongoDB again. I checked that the routes were correct and verified that the MongoDB credentials information were correct. And everything seems to be fine. I still don't know what the problem is.
The confusing thing is that it was working well until I changed the files in folders. I suppose the problem is that the route to some file is wrong, but I haven't found where the problem is.
Edit
I found some posts that say I should move my folders to be directly in the /pages folder, and when I do that my code works perfectly. Why? It's a novice question, but I don't understand how to differentiate what is acting as frontend and what as backend.

How to use data from DB as initial state with Zustand

I'm using the T3 stack (TypeScript, tRPC, Prisma, Next, etc) and I want to either load data from my database using a tRPC query, or use an empty array in my Zustand store. I keep getting an error saying:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app See https://reactjs.org/link/invalid-hook-call for tips about how to debug
and fix this problem. error - TypeError: Cannot read properties of
null (reading 'useContext')
(node:internal/process/task_queues:96:5) { page: '/' }
here's the code generating the error:
type Image = {
url: string;
prompt: string;
};
interface UserState {
images: Image[] | [];
isLoading: boolean;
addImage: (url: string, prompt: string) => void;
removeImage: (url: string, prompt: string) => void;
setLoading: () => void;
}
export const useStore = create<UserState>((set) => {
const { data: sessionData } = useSession();
const dbImages = trpc.images.list.useQuery({
limit: 20,
userId: sessionData?.user?.id ?? "",
}).data?.items;
return {
// initial state
images: dbImages ? dbImages : [],
isLoading: false,
// methods for manipulating state
addImage: (url, prompt) => {
set((state) => ({
images: [
...state.images,
{
url: url,
prompt: prompt,
} as Image,
],
}));
},
removeImage: (url: string) => {
set((state) => ({
images: state.images?.filter((x) => x.url !== url),
}));
},
setLoading: () => {
set((state) => ({
isLoading: !state.isLoading,
}));
},
};
});
What am I doing wrong here? I'm still in the learning phases and would appreciate best practices, etc.

Custom formatter for fastify

I want to add a custom schema formatter for fastify.
import fastify from 'fastify'
import AjvCompiler from '#fastify/ajv-compiler'
const ajvFormatter = AjvCompiler(ajv);
ajvFormatter.addFormat('new-format', /hello/);
const app = fastify({
schemaController: {
compilersFactory: {
buildValidator: ajvFormatter
}
}
})
I add the format but still gives the error:
Failed building the validation schema for POST: /hey, due to error unknown format
"new-format" ignored in schema at path
I guess latest fastify does not support this functionality.
You are using in the wrong way the #fastify/ajv-compiler module. It does not accept an ajv input parameter at all. not it exports an addFormat method.
You need to use the customOption option:
const fastify = require('fastify')
const app = fastify({
logger: true,
ajv: {
customOptions: {
formats: {
'new-format': /hello/,
},
},
},
})
app.get(
'/:hello',
{
schema: {
params: {
type: 'object',
properties: {
hello: {
type: 'string',
format: 'new-format',
},
},
},
},
},
async (request, reply) => {
return request.params
}
)
app.listen(8080, '0.0.0.0')
// curl http://127.0.0.1:8080/hello
// curl http://127.0.0.1:8080/hello-foo

object parsing using req parameter and save in mongoose schema

I have a nested object which I am able to fetch properly but unable to store the the nested object values in the schema. Below are my code snippets:
header.js Router File
router.post('',(req, res)=>{
//console.log(req.body)
const header = new headerModel({
register:{
name: req.body.name,
url: req.body.url
},
logo:{
image: req.body.image,
altText: req.body.altText,
url: req.body.url
}
})
console.log(header)
})
module.exports = router
Header.js Schema File
const mongoose = require('mongoose')
const headerSchema = mongoose.Schema({
register: {
name: {
type: String
},
url: {
type: String
}
},
logo: {
image: {
type: String,
},
altText: {
type: String
},
url: {
type: String
},
}
})
module.exports = mongoose.model('header', headerSchema)
JSON
{
"register":{
"name":"Register",
"url":"/register"
},
"logo":{
"image":"/imagePath/ab.png",
"alttext":"Home",
"url":"/"
}
}
I need to store the value of name and url in register and signin objects respectively in Router File
the header in Router File when logged on console doesn't include register or logo
Because you get the data from JSON in the wrong way and you haven't saved the header yet. You can solve it by:
let header = new headerModel({
register:{
name: req.body.register.name,
url: req.body.register.url
},
logo:{
image: req.body.logo.image,
altText: req.body.logo.altText,
url: req.body.logo.url
}
})
header.save(function (err, doc) {
// Do some thing you want
})
Related information can be found here.

Formik, jest, yup : how to test validation?

i can't find a way to test form yup validation:
it('displays error on submit if name is empty', async () => {
const wrapper = mount(<MyFormik/>)
const getForm = () => wrapper.find('form')
wrapper.find('input[name="name"]').simulate('change', {
persist: () => {},
target: {
name: 'name',
value: ''
}
})
wrapper
.find('MyInnerForm')
.props()
.submitForm()
await wait(0) // await next tick or even 1s...
wrapper.update()
expect(
wrapper
.update()
.find('.error')
.exists()
)
.toBeTruthy() // FALSE!
})
No matter if i wait after submit, update wrapper errors prop is always empty.
And the solution here are not working for me:
https://github.com/jaredpalmer/formik/issues/1146
https://github.com/jaredpalmer/formik/issues/110
Looks like wrapper won't update
Here's the log of formik props after submit:
{ errors: {},
label: '',
name: 'name',
type: 'text',
values: { name: '' },
touched: { name: true },
isValidating: false,
status: undefined,
initialValues: { name: '' },
validateOnChange: true,
validateOnBlur: true } }
...
submitCount: 1,
isValid: false,
You can validate the form values directly on your validation schema.
const yup = require('yup')
const contactSchema = yup.object({
name: yup.string()
.required(),
age: yup.number()
.required()
.positive()
.integer()
})
const errors = await contactSchema.validate({
name: 'Kenneth',
age: -35.5
}).catch(function(err) {
return err
});
console.log("errors", errors);
https://runkit.com/kluplau/5defa8cd122cf6001a3034c7
Without seeing your component I'm not entirely sure what's going wrong. This is likely not to be working:
wrapper
.find('MyInnerForm')
.props()
.submitForm()
If your component MyInnerForm contains a Formik form calling submitForm() there will not cause Formik's validation to run. I would instead do something like this:
wrapper.find("form").simulate("submit");
However if that isn't solving your issue I made a full example that you can have a look at here.

Resources