NEXT, upload file along with metadata (Axios and Formidable with Node JS) - rest

I want to upload a file to NEXT apis along with metadata:
const data = new FormData();
data.append('file', file);
data.append('body', JSON.stringify({ hello: 'world' }));
console.log('Sending');
axios
.post('/api/test-route', data, {
headers: {
'content-type': 'multipart/form-data',
'Authorization': 'json-token',
},
})
.then((response: AxiosResponse) =>
console.log('data = ', response.data)
)
.catch((error: unknown) => console.log(error));
Here's my API Code:
// Backend
import formidable from 'formidable';
import { NextApiRequest, NextApiResponse } from 'next';
import {
errorResponse,
genericResponse,
getErrorDetailsFromKey,
} from '#global-backend/utils/api/responseSynthesizer';
import {
ErrorCodes,
IResponse,
} from '#constants/interfaces/gcorn/backend/apis/response.interfaces';
export const config = {
api: {
bodyParser: false,
},
};
// eslint-disable-next-line import/no-anonymous-default-export
export default async (req: NextApiRequest, res: NextApiResponse) => {
const form = new formidable.IncomingForm();
//#ts-ignore
form.uploadDir = './'; //#ts-ignore
form.keepExtensions = true;
const opsDetails = getErrorDetailsFromKey(
ErrorCodes.INVALID_OR_CORRUPTED_FILE
);
let response = errorResponse({ opsDetails });
let status_code = 400;
const payload: { response: IResponse; status_code: number; error: boolean } =
await new Promise((resolve) => {
let flag = 0;
form.parse(req, (err, _, files) => {
const isError = err?.message !== undefined;
if (isError) {
response = errorResponse({
message: err.message,
opsDetails,
});
status_code = 400;
}
console.log('Files = ', Object.keys(files.file));
const fileCheck = checkImageFileValidity(files.file as unknown as File);
if (fileCheck.error) {
opsDetails.details = fileCheck.message;
response = errorResponse({
message: fileCheck.message,
opsDetails,
});
status_code = 400;
}
response = genericResponse({
status_code: 201,
opsDetails: getErrorDetailsFromKey(
ErrorCodes.FUNFUSE_PROFILE_UPDATE_SUCESS
),
});
status_code = 201;
flag = 1;
resolve({ response, status_code, error: false });
});
});
return res.status(payload.status_code).json(payload.response);
};
const checkImageFileValidity = (
file: File
): { error: boolean; message: string } => {
const { type, size } = file;
// Must be less than 5MBs in Size and Must be Image File
if (size > 5000000)
return { error: true, message: 'File Size More than 5MBs' };
if (!type.includes('image'))
return { error: true, message: 'File is not an image' };
return { error: false, message: 'File is valid' };
};
But for some reason, I don't know how can I parse body part of my form which extracts the info: {hello:world}.
Does anyone know a way to parse it and collect in the backend ?

Assuming everything else is correct, you need to check the _ variable

Related

React Query always returning isError as False

We are using Axios along with react query. Can someone point me to correct example, so that all the react query keys like isError, isSuccess is properly set. As, I read through other answers, the catch block below might be the culprit.
function api<TResponse>({
method,
url,
body,
contentType = 'application/json',
}: IAccessInterceptor): Promise<TResponse> {
return new Promise((resolve, reject) => {
const payload: AxiosRequestConfig = {
method,
url,
headers: {
Accept: contentType,
'content-type': contentType,
},
};
// including 'data' if body is not null
if (body != null) {
payload.data = body;
}
axios(payload)
.then((response: { data: { message: TResponse; data: TResponse } }) => {
resolve(response.data?.message || response.data?.data);
})
.catch((error: { response: { data: IServerError } }) => {
reject(error?.response?.data);
});
});
}
// Some problem in above catch block.
const getABC = (
id: string,
): Promise<IABC> => {
const method = 'GET';
const url = `${BASE_URL}.get_abc?assignment_id=${assignmentId}`;
return api<IABC>({ method, url });
};
export const useABC = (
assignmentId: string | null,
): UseQueryResult<IABC> =>
useQuery<IABC, Error>(
queryKeys.getABC(id),
() => someFunction(id|| ''),
{ enabled: !!id},
);
"useABC().isError" is always false

about vscode api for executeCommand("explorer.newFolder")

I want callback for after newFolder,
executeCommand("explorer.newFolder").then(value => console.log(value))
but The value is not the value of folderName.
I analyzed the code of the command you executed.
The explorer.newFolder command is defined as NEW_FOLDER_COMMAND_ID, and is connected with the openExplorerAndCreate handler. (The result is received through the handler of CommndsRegistry.)
The return of the openExplorerAndCreateis a callback function (Promise).
When i look closely inside,, if the input parameter is not isFolder, that is, if it is a file, a callback function is provided by the return commandService.executeCommand(NEW_UNTITLED_FILE_COMMAND_ID); command.\
but if it is a folder, there is no return value. That is undifined.
in vscode API documentation
return type of executeCommand() is Thenable<T | undefined>. That is, in the case of the corresponding command, it is returned as undefined, not Thenable<T>. If an error is not thrown and undefined is returned, it can be determined that it has been normally executed.
// src/vs/workbench/contrib/files/browser/fileAction.ts
...
export const NEW_FOLDER_COMMAND_ID = 'explorer.newFolder';
...
CommandsRegistry.registerCommand({
id: NEW_FOLDER_COMMAND_ID,
handler: async (accessor) => {
await openExplorerAndCreate(accessor, true);
}
});
...
async function openExplorerAndCreate(accessor: ServicesAccessor, isFolder: boolean): Promise<void> {
const explorerService = accessor.get(IExplorerService);
const fileService = accessor.get(IFileService);
const editorService = accessor.get(IEditorService);
const viewsService = accessor.get(IViewsService);
const notificationService = accessor.get(INotificationService);
const commandService = accessor.get(ICommandService);
const wasHidden = !viewsService.isViewVisible(VIEW_ID);
const view = await viewsService.openView(VIEW_ID, true);
if (wasHidden) {
// Give explorer some time to resolve itself #111218
await timeout(500);
}
if (!view) {
// Can happen in empty workspace case (https://github.com/microsoft/vscode/issues/100604)
if (isFolder) {
throw new Error('Open a folder or workspace first.');
}
return commandService.executeCommand(NEW_UNTITLED_FILE_COMMAND_ID);
}
const stats = explorerService.getContext(false);
const stat = stats.length > 0 ? stats[0] : undefined;
let folder: ExplorerItem;
if (stat) {
folder = stat.isDirectory ? stat : (stat.parent || explorerService.roots[0]);
} else {
folder = explorerService.roots[0];
}
if (folder.isReadonly) {
throw new Error('Parent folder is readonly.');
}
const newStat = new NewExplorerItem(fileService, folder, isFolder);
folder.addChild(newStat);
const onSuccess = async (value: string): Promise<void> => {
try {
const resourceToCreate = resources.joinPath(folder.resource, value);
await explorerService.applyBulkEdit([new ResourceFileEdit(undefined, resourceToCreate, { folder: isFolder })], {
undoLabel: nls.localize('createBulkEdit', "Create {0}", value),
progressLabel: nls.localize('creatingBulkEdit', "Creating {0}", value)
});
await refreshIfSeparator(value, explorerService);
if (isFolder) {
await explorerService.select(resourceToCreate, true);
} else {
await editorService.openEditor({ resource: resourceToCreate, options: { pinned: true } });
}
} catch (error) {
onErrorWithRetry(notificationService, error, () => onSuccess(value));
}
};
await explorerService.setEditable(newStat, {
validationMessage: value => validateFileName(newStat, value),
onFinish: async (value, success) => {
folder.removeChild(newStat);
await explorerService.setEditable(newStat, null);
if (success) {
onSuccess(value);
}
}
});
}

readAsDataUrl function no output

i am using ionic 5 & capacitor.
For some reason, the readAsDataURL isnt working and it isnt showing me an error message either.
the path & file name seem fine, they are :
filePath: file:///storage/emulated/0/Android/data/io.ionic.starter/cache/1763816379-cropped.jpg
path : file:///storage/emulated/0/Android/data/io.ionic.starter/cache/
fileName: 1763816379-cropped.jpg
showCroppedImage(ImagePath) {
var filePath = ImagePath;
let fileName = filePath.split("/").pop();
let path = filePath.substring(0, filePath.lastIndexOf("/") + 1);
alert(filePath);
alert(fileName);
alert(path);
alert("works till here");
this.file
.readAsDataURL(path, fileName)
.then((base64) => {
alert(base64);
})
.catch((err) => {
console.log(err);
alert(err);
});
}
this.file.createFile(
this.file.externalDataDirectory,
"base64string.txt",
true
);
this.file.readAsText(this.file.externalDataDirectory, "base64string.txt");
this.blob = new Blob([this.base64string]);
this.file
.writeFile(
this.file.externalDataDirectory,
"base64string.txt",
this.blob,
{ replace: true, append: false }
)
.then(() => {
alert("File saved in internal storage/android/data/io.starter.ionic/");
});
}
Earlier, my file was getting saved in an internal directory which was not accessible by a user. So i used the externalDataDirectory function, which fixed my issue. It stores the file in an external directory which is accessible by the user.
The file is stored in "internal storage/android/data/#app_id#/"
I ended up sorting the problem for my application and I have included it below:
/** #desc crop the selected image */
async cropImage(fileUrl: any) {
this.show_spinner = true;
await this.ionLoader.showLoader('Cropping Image...');
this.crop.crop(fileUrl, {
quality: 75,
targetHeight: 320,
targetWidth: 240
})
.then(
async (newImage) => {
this.show_spinner = false;
await this.ionLoader.hideLoader();
this.showCroppedImage(newImage.split('?')[0]);
},
async (error: IonicResultMessageInterface) => {
this.show_spinner = false;
await this.ionLoader.hideLoader();
console.error('Error cropping image', error);
console.log(error);
this.setUserProfileImage();
this.toasterService.showToast(error.message, 'failure');
}
);
}
/** #desc show the image after it has been cropped */
async showCroppedImage(ImagePath: string) {
console.log(ImagePath);
this.show_spinner = true;
const copyPath = ImagePath;
const splitPath = copyPath.split('/');
const imageName = splitPath[splitPath.length - 1];
const file_ext = imageName.substr(imageName.lastIndexOf('.') + 1);
try {
const base64 = await Filesystem.readFile({ path: ImagePath});
if (base64) {
console.log(base64);
this.userImageUrl = 'data:image/' + file_ext + ';base64,' + base64.data;
this.show_spinner = false;
await this.updateProfilePictureMethod();
} else {
this.show_spinner = false;
console.log('Error in the showCroppedImage File method');
console.log('Unexpected Error');
this.setUserProfileImage();
this.toasterService.showToast('Unexpected Error Occurred in the showCroppedImage File', 'failure');
}
} catch (error) {
this.show_spinner = false;
console.log('Error in the showCroppedImage File method');
console.log('Unexpected Error');
this.setUserProfileImage();
this.toasterService.showToast(error.message, 'failure');
}
}

Ionic formData append showing null in server

I am trying to upload an image using formData. The api is working fine. But the data is displaying null in the server.
My function is
capture_dl_front(){
this.camera.getPicture(this.cameraOptions)
.then(imageData => {
this.customer.dl_front = normalizeURL(imageData);
this.upload_dl_front(imageData);
}, error => {
this.func.showAlert('Error',JSON.stringify(error));
});
}
upload_dl_front(imageFileUri: any): void {
this.file.resolveLocalFilesystemUrl(imageFileUri)
.then(entry => (<FileEntry>entry).file(file => this.readFile_dl_front(file)))
.catch(err => console.log('Error',JSON.stringify(err)));
}
private readFile_dl_front(file: any) {
const reader = new FileReader();
reader.onloadend = () => {
const imgBlob = new Blob([reader.result], { type: file.type });
this.dl_front_imageUri = imgBlob;
this.dl_front_imageName = file.name;
alert(this.dl_front_imageName)
const img = new FormData();
img.append('image', this.dl_front_imageUri, this.dl_front_imageName)
this.api.test(img).then(data=>alert("final: "+data))
};
reader.readAsArrayBuffer(file);
}
and my api function is
test(image){
let headers = new HttpHeaders({
'Content-Type': 'application/x-www-form-urlencoded',
});
return new Promise( resolve => {
this.http.post(url, image, { headers: headers})
.subscribe(
data => {
resolve(data['message']);
},
error => {
resolve(error.statusText);
}
);
});
}
and i am getting the file in my laravel server as
$image = $request->file('image');
but i am getting null in the image parameter.
What am i doing wrong here?
You should remove the headers in the api call.
test(image){
return new Promise( resolve => {
this.http.post(url, image)
.subscribe(
data => {
resolve(data['message']);
},
error => {
resolve(error.statusText);
}
);
});
}

How to test node.js-mongodb app using Mocha, Chai and Sinon? I am having difficulty with Sinon part

I am new to testing and having difficulty with Sinon stubs and mocks.
How can I write test for 'quote.list_quote' for following senario.
Here is the routes file : quotes.js
const express = require('express');
const request = require('request');
const async = require('async');
const validator = require('validator');
const quote_router = express.Router();
const confg = require("../../confg/confg");
const quote = require("../models/mquotes");
const quotes_model = quote.quotes;
// host name - needs to be set up using the environment variable
const hostname = confg.hostname;
// route for "quotes/"
quote_router.route("/")
// get route : display the random quote
.get((req, res) => {
// display random quote
async.waterfall([
(callback) => {callback(null, {res});},
quote.count_quotes
], check_quote_exist
);
})
// post route : create a new quote
.post((req, res) => {
const doc_json = {author : validator.escape(req.body.quote_author), quote_text : validator.escape(req.body.quote_text)};
const params = {res, doc_json, quote_action : quote.create_quote};
add_edit_quote(params);
})
// put route : edit the quote
.put((req, res) => {
const doc_json = {author : validator.escape(req.body.quote_author), quote_text : validator.escape(req.body.quote_text)};
const params = {res, doc_json, quote_action : quote.update_quote, qid : req.body.quote_id};
add_edit_quote(params);
})
// delete quote : delete the quote
.delete((req, res) => {
const qid = req.body.qid;
const condition = {_id : qid};
async.waterfall([
(callback) => {callback(null, {res, condition});},
quote.delete_quote
], request_quote_list
);
});
// route for "quotes/list" : display quotes list
quote_router.get("/list/", (req, res) => {
// mention the main operation
let operation;
if(req.body.operation != 'undefined') {
operation = req.body.operation;
} else {
operation = "list_quotes";
}
async.waterfall([
(callback) => {callback(null, {res, operation});},
quote.list_quote
], display_quotes_list
);
});
// display the quotes list
const display_quotes_list = (err, params, quotes_list) => {
if (err) {return console.log(err);}
const res = params.res;
const operation = params.operation;
const header_msg = "List of all the quotes";
let alert_msg;
if(operation == "list_quotes") {
alert_msg = null;
} else if(operation == "delete_quote") {
alert_msg = "Quote has been deleted";
}
const params_out = {
page: "quote_list",
title: 'Quotes Manager',
host: hostname,
header_msg,
alert_msg,
quotes_list
};
res.render('index', params_out);
};
// send http request for quote list page
const request_quote_list = (err, params) => {
if (err) {return console.log(err);}
const res = params.res;
const operation = "delete_quote";
request.get('http://' + hostname + '/quotes/list/', {json:{operation}},
(error, response, body) => {
if (!error && response.statusCode == 200) {
res.send(body);
}
});
};
module.exports = quote_router;
This is not complete file. I have included only a portion of it.
And her is the model file : mquotes.js
const mongoose = require('mongoose');
// Define quote schema
const quoteSchema = new mongoose.Schema({
author: String,
quote_text: {type: String, required: true}
},
{timestamps: true}
);
const quote = {};
// Define quotes model
quote.quotes = mongoose.model('quotes', quoteSchema);
// error handler
error_handler = (callback, err, params, return_value) => {
if(err) { return callback(err);}
else {callback(null, params, return_value);}
};
// add quote - create
quote.create_quote = (params, callback) => {
const res = params.res;
const doc_json = params.doc_json;
quote.quotes.create(doc_json, (err, quotes_detail) => {
error_handler(callback, err, {res, operation : 'create_quote'}, quotes_detail);
});
};
// count the number of quotes
quote.count_quotes = (params, callback) => {
quote.quotes.count({}, (err, quotes_count) => {
error_handler(callback, err, params, quotes_count);
});
};
// delete quote - delete - id
quote.delete_quote = (params, callback) => {
quote.quotes.remove(params.condition, (err, query) => {
error_handler(callback, err, params);
});
};
// list quote - find
quote.list_quote = (params, callback) => {
quote.quotes.find({}, (err, quotes_list) => {
error_handler(callback, err, params, quotes_list);
});
};
// find quote by id
quote.quote_by_id = (params, callback) => {
quote.quotes.findById(params.qid, (err, quotes_detail) => {
error_handler(callback, err, params, quotes_detail);
});
};
// returns the detail of random quote
quote.random_qoute = (params, callback) => {
const random_number = params.random_number;
// select one quote after skipping random_number of times
quote.quotes.findOne({}, (err, quotes_detail) => {
error_handler(callback, err, params, quotes_detail);
}).skip(random_number);
};
// update quote - update - id
quote.update_quote = (params, callback) => {
const options = {new: true};
const qid = params.qid;
const update_json = params.doc_json;
quote.quotes.findByIdAndUpdate(qid, {$set: update_json}, options, (err, quotes_detail) => {
params.operation = 'update_quote';
error_handler(callback, err, params, quotes_detail);
});
};
module.exports = quote;
I have installed mocha globally. Now, I want to test the model. Lets take the quote.list_quote function for example.
const mongoose = require('mongoose');
const chai = require('chai');
const sinon = require('sinon');
const expect = chai.expect; // use the "expect" style of Chai
const mquotes = require('./../../app/models/mquotes');
describe('Tests for quote models', () => {
describe("List quote", () => {
it('list_quote() should return list of quotes', () => {
});
});
});
Can anyone tell me about my coding practice too. I mean the way I use functions and modules.
First of all, you should try to use statics methods. And after that, you should use sinon-mongoose and sinon-as-promised if you want to use Promise in mongoose.
And this is my sample code and test with mocha, chai, and sinon. Hope useful for you.
model.js
var Schema = new mongoose.Schema({
name: String,
created_at: {
type: Date,
default: Date.now
},
updated_at: {
type: Date,
default: Date.now
}
});
Schema.statics.findByName = function(name, cb) {
this.findOne({
name: name
})
.exec()
.then(function getTemplate(template) {
if (!template) {
var error = new Error('Not found template by name: "' + name + '"');
error.status = 404;
return cb(error);
}
return cb(null, template);
})
.catch(function catchErrorWhenFindByTemplateName(error) {
error.status = 500;
return cb(error);
});
}
module.exports = mongoose.model('model', Schema);
test.js
var expect = require('chai').expect,
sinon = require('sinon'),
mongoose = require('mongoose');
require('sinon-as-promised');
require('sinon-mongoose');
var Model = require('../../app/models/model');
describe('Model', function () {
describe('static methods', function () {
describe('#findByName', function () {
var ModelMock;
beforeEach(function () {
ModelMock = sinon.mock(Model);
});
afterEach(function () {
ModelMock.restore();
});
it('should get error status 404 if not found template', function (done) {
var name = 'temp';
ModelMock
.expects('findOne').withArgs({name: name})
.chain('exec')
.resolves(null);
Model.findByName(name, function (error) {
expect(error.status).to.eql(404);
ModelMock.verify();
done();
});
});
it('should get message not found template if name is not existed', function (done) {
var name = 'temp';
ModelMock
.expects('findOne').withArgs({name: name})
.chain('exec')
.resolves(null);
Model.findByName(name, function (error) {
expect(error.message).to.match(/Not found template by name/gi);
ModelMock.verify();
done();
});
});
it('should get template when name is existed', function (done) {
var name = 'temp';
ModelMock
.expects('findOne').withArgs({name: name})
.chain('exec')
.resolves('SUCCESS');
Model.findByName(name, function (error) {
expect(error).to.be.null;
ModelMock.verify();
done();
});
});
it('should get error status 500 when model crashed', function (done) {
var name = 'temp';
ModelMock
.expects('findOne').withArgs({name: name})
.chain('exec')
.rejects(new Error('Oops! Crashed'));
Model.findByName(name, function (error) {
expect(error.status).to.eql(500);
ModelMock.verify();
done();
});
});
});
});
});