How to Use Rest api with React Native; Network Call Issues - mongodb

I am newbie in React Native,
I made a simple back-end using Mongodb and express routes etc in MongoDb atlas. I am successfully able to post/get/patch/Delete operation on mongodb atlas that store Title and Description using Postman. Everything is working fine.
Here comes the problem First when i make a simple frontend in ReactNative that take inputs Title and Description. I want application that take simple input of Title and Description and on Submit Button it store into the the mongodb Atlas just like postman is doing. I tried but its not working code is below. I dont know how to communicate the front end into backend. I watch alot of tutorials but unable to get the point.
Secondly, when i make a server i wrote in pakage.json > "start": "nodemone server.js" and i need to run ReactNative app i update the pakage.json > "start": "expo start" to run app. How can i run server and expo app same time? if i seprate the app folder then how can i connect both of them.
below is my Code.
Routes folder post.js
const express = require( 'express' );
const router = express.Router();
const Post = require ('../models/Post')
//Gets back all the posts
router.get ( '/', async (req, res) =>{
try{
const post = await Post.find();
res.json(post);
}catch (err) {
res.json({message: err })
}
});
//To Submit the Post
router.post('/', async (req, res) =>{
//console.log(req.body);
const post = new Post({
title: req.body.title,
description: req.body.description
});
try{
const savedPost = await post.save();
res.json(savedPost);
}catch (err) {
res.json ({ message: err })
}
});
//Get back specific Post
router.get('/:postId', async (req, res) =>{
try{
const post= await Post.findById(req.params.postId);
res.json(post);
}catch(err) {
res.json({message: err });
}
})
// to delete specific post
router.delete('/:postId', async (req, res) =>{
try{
const removePost= await Post.remove({_id: req.params.postId});
res.json(removePost);
}catch(err) {
res.json({message: err });
}
})
//update Post
router.patch('/:postId', async (req, res) =>{
try{
const updatePost = await Post.updateOne(
{_id: req.params.postId},
{ $set:
{title: req.body.title}
});
res.json(updatePost);
}catch(err) {
res.json({message: err });
}
})
module.exports = router;
Defined Schema Post.js
const mongoos = require( 'mongoose' );
const PostSchema = mongoos.Schema ({
title: {
type: String,
required: true
},
description: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
}
})
module.exports = mongoos.model ('Post', PostSchema); // giving this schma name Post
server.js
const express = require( 'express' );
const app = express();
var mongo = require('mongodb');
const mongoos = require( 'mongoose' );
const bodyParser = require('body-parser');
require('dotenv/config');
const postRoute = require('./Routes/post');
app.use(bodyParser.json());
app.use ('/post', postRoute);
app.get ( '/', (req, res) =>{
res.send('We are on Home ')
});
// connecting to database
mongoos.connect(
process.env.DB_CONNECTION,
{ useNewUrlParser: true },
() => console.log('Connected to db')
);
app.listen(3000);
Frontend Form.js
import React from 'react';
import { StyleSheet, Text, View, TextInput, TouchableOpacity } from 'react-native';
class Form extends React.Component{
constructor(){
super();
this.State = {
title: '',
description: ''
}
}
getInput(text, field){
if(field == 'title')
{
this.setState({ title: text, })
}
else if(field == 'description')
{
this.setState({ description: text, })
}
//console.warn(text)
}
submit(){
let collection={}
collection.title = this.state.title,
collection.description = this.state.description;
console.warn(collection);
var url = process.env.DB_CONNECTION ;
fetch(url, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
collection
}),
});
}
render() {
return (
<View style={styles.container}>
<TextInput style={styles.inputBox}
underlineColorAndroid= 'rgba(0,0,0,0)'
placeholder='Title'
selectionColor="#fff"
keyboardType="default"
onChangeText = {(text) => this.getInput(text, 'title')}
/>
<TextInput style={styles.inputBox}
multiline = {true}
numberOfLines = {4}
underlineColorAndroid= 'rgba(0,0,0,0)'
placeholder='Description'
selectionColor="#fff"
keyboardType="default"
onChangeText= {(text) => this.getInput(text, 'description')}
/>
<TouchableOpacity onPress={()=>this.submit()} style={styles.btn} >
<Text style={{textAlign: 'center'}}>Submit</Text>
</TouchableOpacity>
</View>
);
}
}
export default Form;

Here comes a very basic solution to your problem:
1: if you are using Rest API based model of communication go for Two separate repos on GITHUB. One for React native app of yours and one for server-side of yours.
2: now to go to Heroku.com and make an app there and attach your card there in order to use the full Free Sandbox functionality
3: create a project there and find an option to deploy from Github.
4: for data communication aka network requests its easy to use axios rather than Fetch
for best practices use :
https://riptutorial.com/react-native/topic/857/getting-started-with-react-native
5: in order to run more than one command in package json able to run multiple scripts in package.json you can either do it like
scripts:{"run": "yarn start" && "react-native-expo"}
6: or if your scripts are like they gonna need to run constantly in the background it's better that you create two separate scripts
scripts:{"run1": "yarn start", "run2":"yarn start2"}
7: I see you are not handling the AsyncAwait Try catch or Promise after the fetch
8: you are also not hitting the server-side URL seemingly you are hitting DB connection url. what you should be doing is that you hit the POST/GET/UPDATE routing endpoint of yours

Related

Create a user in JasperReports Server using Axios

I am trying to create a user in JRS using Axios, I have managed to run reports but now I am trying to create users, my code is as follows, is there any reason why it does not work?
const express = require('express');
const axios = require("axios");
const app = express();
let bodyParser = require("body-parser");
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended:true}))
app.get('/prueba', function(req,res){
res.sendFile(__dirname+"/prueba.html");
});
app.post('/form-submit', async function(req,res){
try {
const url = "http://localhost:8080/jasperserver/rest_v2/users/jose"
let params = {
fullName:"Joe User",
emailAddress:"juser#example.com",
externallyDefined:false,
enabled:false,
password:"mySecretPassword",
roles:[
{"name":"ROLE_USER", "tenantId":"organización_1"}]
}
const file = await axios.put(url, {
params: params,
responseType: "stream",
auth:{
username: "jasperadmin",
password: "jasperadmin"
}
})
res.writeHead(200,{"Content-Type":"application/json"})
file.data.pipe(res)
} catch (error) {
console.log(error)
}
});
app.listen(7000)

Unit and integration test of Express REST API and multer single file update middleware

Introduction
Hello everybody,
I'm pretty new to unit and integration testing. The current REST API I'm working on involves file uploads and file system. If you want me to explain what's API this is, I can explain it to you using few sentences. Imagine a system like Microsoft Word. There are only users and users have documents. Users' documents are only JSON files and they are able to upload JSON file to add a document. My API currently has 3 routes, 2 middlewares.
Routes:
auth.js (authorization route)
documents.js (document centered CRUD operations)
users.js
Middlewares:
auth.js (To check if there is valid JSON web token to continue)
uploadFile.js (To upload single file using multer)
I have been able to unit/integration test auth.js, users.js routes and auth.js middleware. These routes and middlewares were only involving small packages of data I/O, so they were pretty easy for me. But documents.js router and uploadFile.js middleware is pretty hard for me to overcome.
Let me share my problems.
Source codes
documents.js Router
.
.
.
router.post('/mine', [auth, uploadFile], async (req, res) => {
const user = await User.findById(req.user._id);
user.leftDiskSpace(function(err, leftSpace) {
if(err) {
return res.status(400).send(createError(err.message, 400));
} else {
if(leftSpace < 0) {
fs.access(req.file.path, (err) => {
if(err) {
res.status(403).send(createError('Your plan\'s disk space is exceeded.', 403));
} else {
fs.unlink(req.file.path, (err) => {
if(err) res.status(500).send('Silinmek istenen doküman diskten silinemedi.');
else res.status(403).send(createError('Your plan\'s disk space is exceeded.', 403));
});
}
});
} else {
let document = new Document({
filename: req.file.filename,
path: `/uploads/${req.user.username}/${req.file.filename}`,
size: req.file.size
});
document.save()
.then((savedDocument) => {
user.documents.push(savedDocument._id);
user.save()
.then(() => res.send(savedDocument));
});
}
}
});
});
.
.
.
uploadFile.js Middleware
const fs = require('fs');
const path = require('path');
const createError = require('./../helpers/createError');
const jsonFileFilter = require('./../helpers/jsonFileFilter');
const multer = require('multer');
const storage = multer.diskStorage({
destination: function(req, file, cb) {
console.log('file: ', file);
if(!req.user.username) return cb(new Error('Dokümanın yükleneceği klasör için isim belirtilmemiş.'), null);
let uploadDestination = path.join(process.cwd(), 'uploads', req.user.username);
fs.access(uploadDestination, (err) => {
if(err) {
// Directory with username doesn't exist in uploads folder, so create one
fs.mkdir(uploadDestination, (err) => {
if(err) cb(err, null);
cb(null, uploadDestination);
});
} else {
// Directory with username exists
cb(null, uploadDestination);
}
});
},
filename: function(req, file, cb) {
cb(null, `${file.originalname.replace('.json', '')}--${Date.now()}.json`);
}
});
module.exports = function(req, res, next) {
multer({ storage: storage, fileFilter: jsonFileFilter }).single('document')(req, res, function(err) {
if(req.fileValidationError) return res.status(400).send(createError(req.fileValidationError.message, 400));
else if(!req.file) return res.status(400).send(createError('Herhangi bir doküman seçilmedi.', 400));
else if(err instanceof multer.MulterError) return res.status(500).send(createError(err.message, 500));
else if(err) return res.status(500).send(createError(err, 500));
else next();
});
}
Questions
1. How can I test user.leftDiskSpace(function(err, leftSpace) { ... }); function which has a callback and contains some Node.js fs methods which also has callbacks?
I want to reach branches and statements user.leftDiskSpace() function containing. I thought of using mock functions to mock out the function but I don't know how to do so.
2. How to change multer disk storage's upload destination for a specified testing folder?
Currently my API uploads the test documents to development/production uploads disk storage destination. What is the best way to change upload destination for testing? I thought to use NODE_ENV global variable to check if the API is being tested or not and change destination in uploadFile.js middleware but I'm not sure if it's a good solution of this problem. What should I do?
Current documents.test.js file
const request = require('supertest');
const { Document } = require('../../../models/document');
const { User } = require('../../../models/user');
const mongoose = require('mongoose');
const path = require('path');
let server;
describe('/api/documents', () => {
beforeEach(() => { server = require('../../../bin/www'); });
afterEach(async () => {
server.close();
await User.deleteMany({});
await Document.deleteMany({});
});
.
.
.
describe('POST /mine', () => {
let user;
let token;
let file;
const exec = async () => {
return await request(server)
.post('/api/documents/mine')
.set('x-auth-token', token)
.attach('document', file);
}
beforeEach(async () => {
user = new User({
username: 'user',
password: '1234'
});
await user.save();
token = user.generateAuthToken();
file = path.join(process.cwd(), 'tests', 'integration', 'files', 'test.json');
});
it('should return 400 if no documents attached', async () => {
file = undefined;
const res = await exec();
expect(res.status).toBe(400);
});
it('should return 400 if a non-JSON document attached', async () => {
file = path.join(process.cwd(), 'tests', 'integration', 'files', 'test.png');
const res = await exec();
expect(res.status).toBe(400);
});
});
});

Submit Data in mongoDB

I am trying to submit my data using mongoose in my NEXT app, I am able to fetch data using getServerSideProps like
export async function getServerSideProps(context) {
const data = await shop.findOne({ shopName: "testing" });
return {
props: {
dbData: JSON.stringify(data),
}, // will be passed to the page component as props
};
}
Now I want to put some data in my mongoDb is there any method that will run after my button click and renders server side code just like getServerSideProps in which i can run my mongoose query?
Try this way in your api controller
const handler = nextConnect();
//Shop Schema
const Shop = mongoose.model('Shop',{
shopName: String,
id: String
});
//Post request handeler
handler.post(async (req, res) => {
var shop = new Shop({
shopName: req.body.name,
id:req.body.id
})
let data = await shop.save();
res.json({message: 'ok'});
})

mongodb jest error ReferenceError: You are trying to `import` a file after the Jest environment has been torn down

I am trying to test basic mongoDB operation using Jest.
it('should insert a record into collection', async () => {
const users = await db.collection('mariochar');
const mockUser = { name: 'marioChar3'};
await users.insertOne(mockUser);
const insertedUser = await users.findOne({name: 'marioChar3'});
expect(insertedUser).toEqual(mockUser);
});
If I use the default Jest suggested template as above, tests are passing correctly and records can also be seen in the database.
However, since I was learning from this youtube tutorial, I tried to test the Schema directly as shown in the video like this:
it('should insert a mario into collection', async () => {
const MarioCharSchema = new Schema({
name: String,
weight: Number
})
const MarioChar = await mongoose.model('mariochar', MarioCharSchema)
const char = await new MarioChar({ name: "Mario" })
char.save()
console.log(char.isNew)
expect(char.isNew).toBeTruthy()
});
I get the error: ReferenceError: You are trying toimporta file after the Jest environment has been torn down.
After viewing few posts in stackoverflow, I added the fake timer and I am posting the complete code below. It did not get rid of the error.
My second question is that even though Jest shows the above error and also npm ERR! Test failed. See above for more details., it shows the test as passed. Is there a way to report it as failed instead since it is easy to mistake it as passed when there will be a lot of tests?
const { MongoClient } = require('mongodb');
const mongoose = require('mongoose')
const Schema = mongoose.Schema
jest.useFakeTimers()
describe('insert', () => {
let connection;
let db;
beforeAll(async () => {
jest.useFakeTimers()
connection = await MongoClient.connect("mongodb://localhost/testaroo", {
useNewUrlParser: true,
useUnifiedTopology: true
});
db = await connection.db();
});
afterAll(async () => {
await connection.close();
});
it('should insert a mario into collection', async () => {
const MarioCharSchema = new Schema({
name: String,
weight: Number
})
const MarioChar = await mongoose.model('mariochar', MarioCharSchema)
const char = await new MarioChar({ name: "Mario" })
char.save()
expect(char.isNew).toBeTruthy()
});
});
jest.config.js
module.exports = {
"preset": '#shelf/jest-mongodb',
"timers": "fake",
"testEnvironment": "node"
};
package.json
...
"dependencies": {
"mongoose": "^5.9.18"
},
"devDependencies": {
"#shelf/jest-mongodb": "^1.1.5",
"jest": "^26.0.1"
}
...
full error:
ReferenceError: You are trying to `import` a file after the Jest environment has been torn down.
at model.Document.$__getAllSubdocs (node_modules/mongoose/lib/document.js:2890:37)
at _getPathsToValidate (node_modules/mongoose/lib/document.js:2159:23)
at model.Document.$__validate (node_modules/mongoose/lib/document.js:2314:23)
at node_modules/kareem/index.js:369:33
at node_modules/#jest/fake-timers/build/legacyFakeTimers.js:496:18
PASS test/saving.test.js

How to use mockgoose (or any other db mocking) in express app integration test

Using mockgoose in a simple unit test is quite straight-forward. However I'm a bit fuzzy as to how one would go about using mockgoose or other mocking solutions in an acceptance or integration test.
Given a simple express/MongoDB app like the following:
/*app.js*/
const express = require('express')
const app = express()
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
var greetingSchema = mongoose.Schema({
greeting: String
});
var Greeting = mongoose.model('Greeting', greetingSchema);
app.get('/', function (req, res) {
Greeting.find({greeting: 'Hello World!'}, function (err, greeting){
res.send(greeting);
});
});
app.listen(3000, function () {
console.log('Example app listening on port 3000!')
})
and a simple integration test like this:
/*test.js*/
const app = require('app.js');
const request = require('supertest');
it('sends "Hello World!" on the response body', (done) => {
request(app)
.get('/')
.expect(200, 'Hello World!', done);
});
});
By using the actual app in the request, we are connecting to the app's database ('mongodb://localhost/test'). How then can one use mockgoose, or any other solution, to mock the MongoDB database and still run an integration test like the one shown above?
I had the same problem as you. In my case, I solved using chai + chai-http and breaking the db connection and app in different files:
db.js:
const mongoose = require('mongoose');
const config = require('../../config');
mongoose.Promise = global.Promise;
mongoose.set('debug', process.env.DEBUG != undefined);
function open(){
return new Promise((resolve, reject) => {
if(process.env.DEBUG != undefined) {
let Mockgoose = require('mockgoose').Mockgoose;
let mockgoose = new Mockgoose(mongoose);
mockgoose.helper.setDbVersion("** your mongodb version **");
mockgoose.prepareStorage().then(function() {
mongoose.connect(config.db_test, (err, res) => {
if (err) return reject(err);
resolve();
});
}).catch(reject);
}else{
mongoose.connect(config.db, (err, res) => {
if (err) return reject(err);
resolve();
});
}
});
}
function close(){
return mongoose.disconnect();
}
module.exports = { close, open };
app.js:
const express = require('express');
const bodyParser = require('body-parser');
const api = require('./routes');
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use('/api', api);
module.exports = app;
test.js (for test):
const chai = require('chai');
const chaiHttp = require('chai-http');
const expect = chai.expect;
const conn = require('./../utils/db'); // <-- db.js
const app = require('../../app'); // <-- app.js
chai.use(chaiHttp);
describe('# Test', function(){
before(function(done) {
conn.open().then(() => done()).catch(done);
});
after(function(done){
conn.close().then(() => done()).catch(done);
});
it(`test something`, function(done){
chai.request(app) // <-- pass the app here
.get('/path/to/test')
.then((res) => {
// expects
done();
})
.catch((err) => {
done(err);
});
});
});
index.js (for development or production):
const conn = require('./utils/db'); // <-- db.js
const app = require('./app'); // <-- app.js
const config = require('./config');
conn.open().then(() => {
app.listen(config.port, () => {
// OK!
});
});
I hope it works for you or anyone.