Is it possible to use Probot to create an issue on response from an express route? I've tried the following, but they async function can't access context.github.
const createIssue = async function (issue, context) {
const owner = issue.owner; const repo = issue.repo; const title = issue.title; const body = issue.body; const assignees = issue.assignees; const labels = issue.labels
return context.github.issues.create({ owner, repo, title, body, labels, assignees })
}
const router = app.route('/robot')
router.use(require('express').static('public'))
router.get('/test', (req, res) => {
const issue = {
repo: 'reponame',
owner: 'ownername',
title: req.query.title,
labels: req.query.lab,
body: req.query.body,
assignees: req.query.as
}
createIssue(issue, context).then(
res.send('Success')
).catch(err => console.log(err))
})
I think this might be what you are looking for: https://probot.github.io/api/latest/classes/application.html#auth
const createIssue = async function (issue, app) {
const github = await app.auth();
const owner = issue.owner; const repo = issue.repo; const title = issue.title; const body = issue.body; const assignees = issue.assignees; const labels = issue.labels
return github.issues.create({ owner, repo, title, body, labels, assignees })
}
const router = app.route('/robot')
router.use(require('express').static('public'))
router.get('/test', (req, res) => {
const issue = {
repo: 'reponame',
owner: 'ownername',
title: req.query.title,
labels: req.query.lab,
body: req.query.body,
assignees: req.query.as
}
const
createIssue(issue, app).then(
res.send('Success')
).catch(err => console.log(err))
})
Related
I have a project in Node JS in which I have a form to add new users.
How can I view this information in JSON format?
These are the data that I see:
name age country city
------------------------------
user1 22 Spain Madrid button{View JSON}
When I press the 'View JSON' button, the following must be displayed below the table:
[
"id": 1,
"name": "user1",
"age": 22,
"country": "Spain" {
"city":"Madrid"
}
]
My problem: how can I create a function that performs this conversion? How do I call the function from index.ejs?
I cleared and merged the codes. And I created a new endpoint as /export to export the data as CSV file. I couldn't test it so let me know if it doesn't work.
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const MongoClient = require('mongodb').MongoClient;
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(express.static('public'));
app.set('views', './src/views');
app.get('/', async (req, res) => {
const db = await mongoDB();
const person = await db.collection('person').find().toArray();
res.render('index.ejs', { person: person })
})
app.get('/export', async (req, res) => {
await convertCSV();
res.status(200).send( { success: 1 });
})
app.post('/person', async (req, res) => {
res.redirect('/');
})
app.listen(process.env.PORT, function () {
console.log(`server: http://${process.env.HOST}:${process.env.PORT}`);
})
const mongoDB = () => {
return new Promise((resolve, reject) => {
const url = 'mongodb://127.0.0.1:27017';
MongoClient.connect(url, { useUnifiedTopology: true })
.then(client => {
const db = client.db('users')
resolve(db);
})
.catch(error => reject(error))
});
}
const convertCSV = () => {
return new Promise((resolve, reject) => {
const converter = require("json-2-csv");
const fetch = require("node-fetch");
const fs = require("fs");
const flatten = require('flat');
const maxRecords = 10;
const getJson = async () => {
const response = await fetch(`http://${process.env.HOST}:${process.env.PORT}/users.json`);
const responseJson = await response.json();
return responseJson;
};
const convertToCSV = async () => {
const json = await getJson();
let keys = Object.keys(flatten(json[0]));
let options = {
keys: keys
};
converter.json2csv(json, json2csvCallback, options);
};
let json2csvCallback = function (err, csv) {
if (err) throw err;
const headers = csv.split('\n').slice(0, 1);
const records = csv.split('\n').slice(0,);
for (let i = 1; i < records.length; i = i + maxRecords) {
let dataOut = headers.concat(records.slice(i, i + 3)).join('\n');
let id = Math.floor(i / maxRecords) + 1;
fs.writeFileSync('data' + id + '.csv', dataOut)
}
};
await convertToCSV();
resolve();
})
}
However, it is not a good practice at all to using controller, index and route in the same file. A better approach would be to create routes, controllers folders and put the codes in a more orderly form.
Something like this (You can find better ones of course mine is just advice):
- index.js
- router.js (A router to manage your endpoints)
- controllers (Controller when you call the endpoint)
-> export.controller.js
-> person.controller.js
- routes (Endpoints)
-> export.route.js
-> person.route.js
- helpers
-> databaseHandler.js (Database connection handler)
it's nice to join the group of people brave enough to ask the questions on Stack, so that everyone cantake advantage :)
My problem is pretty strange. I'm writing an app in Express, I have two routes so far and everything is going pretty smoothly, yet I've encountered one problem, which I can not seem to solve. In one route, with the patch method, the incoming requests have emmpty body. The rest of app is running smoothly, everything is working fine and this one route seems to be broken, I can not figure out why. Strange enough, yet I found out that the requests DO have body in one case - when I'm sending requests with my tests (supertest) using the .send({ ... }) method. When I'm sending requests with .attach or .field - they come empty. Same with requests sent from Postman (empty). What is causing such strange behavior?
Here are my tests:
const request = require('supertest');
const Image = require('../models/image');
const app = require('../app');
const crypto = require('crypto');
const fs = require('fs')
const { setupImages } = require('./fixtures/db')
beforeEach(setupImages);
describe('[IMAGE] - ', () => {
test('Should get images', async () => {
const main_img = await Image.findOne({ main: true });
const image = await request(app)
.get(`/image/${main_img._id}`)
.expect(200);
expect(image.header['content-type']).toBe('image/png');
});
test('Should delete images', async () => {
const image = await Image.findOne({ description: 'Lorem ipsum' });
await request(app)
.delete(`/image/${image._id}`);
const imageFound = await Image.findById(image._id);
expect(imageFound).toBeNull();
});
//TEST THAT FAILS
test('Should edit images', async () => {
const image = await Image.findOne({ main: false });
await request(app)
.patch(`/image/${image._id}`)
.field('description', 'new desc')
.attach('image', './src/tests/fixtures/imgtest.png')
.expect(200);
const returnChecksum = file => {
return crypto
.createHash('md5')
.update(file, 'utf8')
.digest('hex')
}
const imageEdited = await Image.findById(image._id);
const newImageChecksum = returnChecksum(fs.readFileSync(__dirname + '/fixtures/imgtest.png'));
expect(returnChecksum(imageEdited.image)).toBe(newImageChecksum);
expect(imageEdited.description).toBe('new desc');
});
})
Here are image routes
const express = require('express');
const router = new express.Router();
const Image = require('../models/image');
const chalk = require('chalk');
router.get('/image/:id', async (req, res) => {
const { id } = req.params;
try {
const image = await Image.findById(id);
if (!image) {
return res.status(404).send()
}
res.set('Content-Type', 'image/png');
res.send(image.image);
} catch (e) {
console.log(chalk.red('Error serving image: ') + e);
res.send(500);
}
});
//THE ROUTE THAT FAILS
router.patch('/image/:id', async (req, res) => {
const { id } = req.params;
const updateFields = Object.entries(req.body);
console.log('image patch req body', req.body)
try {
const imageEdited = await Image.findById(id, function (err, doc) {
if (err) { return err; }
updateFields.forEach(field => doc[field[0]] = field[1])
doc.save(res.status(200).send(doc));
});
if (!imageEdited) {
res.status(400).send();
}
} catch (e) {
res.status(500).send();
console.log(chalk.red('Error editing image: ') + e);
}
});
router.delete('/image/:id', async (req, res) => {
const { id } = req.params;
try {
await Image.findByIdAndDelete(id);
res.status(200).send();
} catch (e) {
res.status(500).send();
console.log(chalk.red('Error deleting image: ') + e);
}
});
module.exports = router;
And my app.js file:
const express = require('express');
require('./db/mongoose');
const productRouter = require('./routers/product');
const imageRouter = require('./routers/image');
const app = express();
app.use(express.json());
app.use(productRouter);
app.use(imageRouter);
module.exports = app;
The result of console.log in image route:
console.log src/routers/image.js:30
image patch req body {}
And this is the behavior of the app with changed sending method in test:
test('Should edit images', async () => {
const image = await Image.findOne({ main: false });
await request(app)
.patch(`/image/${image._id}`)
// .field('description', 'new desc')
// .attach('image', './src/tests/fixtures/imgtest.png')
.send({description: 'new desc'})
.expect(200);
const returnChecksum = file => {
return crypto
.createHash('md5')
.update(file, 'utf8')
.digest('hex')
}
const imageEdited = await Image.findById(image._id);
const newImageChecksum = returnChecksum(fs.readFileSync(__dirname + '/fixtures/imgtest.png'));
expect(returnChecksum(imageEdited.image)).toBe(newImageChecksum);
expect(imageEdited.description).toBe('new desc');
});
console.log src/routers/image.js:30
image patch req body { description: 'new desc' }
Thanks in advance!
I've managed to fix it myself, the problem lied in fact that I've forgot about multer. So this strange behavior can be result of not using said library, even if you're not sending any files apparently.
I am trying to have a flexible Cloud Function that executes on different end points.
My original Cloud Function looks like this:
const functions = require('firebase-functions')
const admin = require('firebase-admin')
const _ = require('lodash')
const { getObjectValues } = require('./helper-functions.js')
admin.initializeApp()
const json2csv = require('json2csv').parse
exports.csvJsonReport = functions.https.onRequest((request, response) => {
const db = admin.firestore()
const userAnswers = db.collection('/surveys/CNA/submissions')
return (
userAnswers
.get()
// eslint-disable-next-line promise/always-return
.then(querySnapshot => {
let surveySubmissions = []
querySnapshot.forEach(doc => {
const userSubmission = doc.data()
surveySubmissions.push({
..._.mapValues(userSubmission.answers, getObjectValues), // format answers
...userSubmission.anonUser,
})
})
const csv = json2csv(surveySubmissions)
response.setHeader('Content-disposition', 'attachment; filename=cna.csv')
response.set('Content-Type', 'text/csv')
response.status(200).send(csv)
})
.catch(error => {
console.log(error)
})
)
})
I am trying to extend this function to work on multiple collections. In the above function I am targeting the CNA collection. so instead of db.collection('/surveys/CNA/submissions/') I would like it to be db.collection('/surveys/:surveyId/submissions/')
Below is my attempt at trying to extend my original Cloud Function:
const functions = require('firebase-functions')
const admin = require('firebase-admin')
const express = require('express')
const bodyParser = require('body-parser')
const _ = require('lodash')
const { getObjectValues } = require('./helper-functions.js')
admin.initializeApp(functions.config().firebase)
const db = admin.firestore()
const app = express()
const main = express()
main.use('/api/v1', app)
main.use(bodyParser.json())
exports.webApi = functions.https.onRequest(main)
app.get('surveys/:id', (request, response) => {
const surveyId = request.query
const userAnswers = db.collection(`/survey/${surveyId}/submissions`)
return (
userAnswers
.get()
// eslint-disable-next-line promise/always-return
.then(querySnapshot => {
let surveySubmissions = []
querySnapshot.forEach(doc => {
const userSubmission = doc.data()
surveySubmissions.push({
..._.mapValues(userSubmission.answers, getObjectValues), // format answers
...userSubmission.anonUser,
})
})
const csv = json2csv(surveySubmissions)
response.setHeader('Content-disposition', 'attachment; filename=cna.csv')
response.set('Content-Type', 'text/csv')
response.status(200).send(csv)
})
.catch(error => {
console.log(error)
})
)
})
When I request my endpoint: myapp.firebaseapp.com/api/v1/surveys/CNA
Cannot GET /api/v1/surveys/CNA is shown in my browser.
Could someone please point me in the right direction?
To crate a GET /survey/:id endpoint in order to fetch a submission by id, use the following code in your new Cloud Function:
app.get('surveys/:id', (request, response) => {
const surveyId = request.params.id
const userAnswers = db.collection(`/survey/${surveyId}/submissions`)
Let me know if it works for you.
I have a query, that is how I can send "userURL" variable from below file(imageController.js) to another file(contactController.js). and one thing I want to send only "userURL" variable not the whole function "resize". I tried a lot to solve this issue by using "module.exports" but the problem I got is that "module.exports" sending the whole function "resize" not the "userURL" variable. now in second file "contactController.js" where i have mentioned {userURL} in console.log but it's printing only "your result undefined". I just want to export userURL from first file imageController.js to second file "contactController.js".
imageController.js
exports.resize = async (req, res, next) => {
if(!req.file){
next()
return
}
const extension = req.file.mimetype.split('/')[1]
req.body.userFile = `${uuid.v4()}.${extension}`
const photo = await jimp.read(req.file.buffer)
await photo.resize(500, jimp.AUTO)
await photo.write(`./public/uploads/${req.body.userFile}`)
const userimg = photo;
console.log(`./public/uploads/${req.body.userFile}`)
cloudinary.config({
cloud_name: 'katal',
api_key: process.env.API_KEY,
api_secret: process.env.API_SECRET
});
cloudinary.v2.uploader.upload(`./public/uploads/${req.body.userFile}`,{
transformation: { width:100, height:100}}, function(error, result) {
console.log('please show result\t' +JSON.stringify(result))
const userURL ='vikivivki'
res.send(result.secure_url)
console.log(result.secure_url)
})
}
contactController.js
const mongoose = require('mongoose')
const Contact = mongoose.model('Contact')
const moment = require('moment-timezone')
const {userURL} = require('./imageController')
exports.contactForm = (req, res) => {
res.render('contact')
}
exports.usermessage = async(req, res) => {
req.body.name = req.body.name
req.body.email = req.body.email
req.body.message = req.body.message
const ind = moment.tz(Date.now(), "Asia/Calcutta")
const newContact = new Contact(req.body)
await newContact.save()
console.log('your result',userURL)
let showResult1 = JSON.stringify(newContact)
let showResult = JSON.parse(showResult1)
res.send(showResult.message)
}
You need to export items you need access to
exports.userURL ='vikivivki'; //exporting here along with rezise as well
file imageController.js
//imageController.js
exports.userURL ='vikivivki'; //exporting here along with rezise as well
exports.resize = async (req, res, next) => {
if(!req.file){
next()
return
}
const extension = req.file.mimetype.split('/')[1]
req.body.userFile = `${uuid.v4()}.${extension}`
const photo = await jimp.read(req.file.buffer)
await photo.resize(500, jimp.AUTO)
await photo.write(`./public/uploads/${req.body.userFile}`)
const userimg = photo;
console.log(`./public/uploads/${req.body.userFile}`)
cloudinary.config({
cloud_name: 'katal',
api_key: process.env.API_KEY,
api_secret: process.env.API_SECRET
});
cloudinary.v2.uploader.upload(`./public/uploads/${req.body.userFile}`,{
transformation: { width:100, height:100}}, function(error, result) {
console.log('please show result\t' +JSON.stringify(result))
const userURL ='vikivivki'
res.send(result.secure_url)
console.log(result.secure_url)
})
}
In your contactController.js
//contactController.js
const mongoose = require('mongoose')
const Contact = mongoose.model('Contact')
const moment = require('moment-timezone')
const {userURL} = require('./imageController'); // Please have a check on location if they are in the same directory
exports.contactForm = (req, res) => {
res.render('contact')
}
exports.usermessage = async(req, res) => {
req.body.name = req.body.name
req.body.email = req.body.email
req.body.message = req.body.message
const ind = moment.tz(Date.now(), "Asia/Calcutta")
const newContact = new Contact(req.body)
await newContact.save()
console.log('your result',userURL)
let showResult1 = JSON.stringify(newContact)
let showResult = JSON.parse(showResult1)
res.send(showResult.message)
}
I am trying to implement separation of concerns by using export module. All the code is working if used without separation of concern but as soon as I am trying to import generateUrlArray() from const db = require('../db') nothing is working. Nodejs is not giving me any error on the back-end. The error I am getting on front-end is Error: SyntaxError: Unexpected end of JSON input . I am positive that the error is coming from back-end. Let me know if you have any ideas.
controller.js
const db = require('../db')
exports.getWebApiList = (req, res) => {
(async function fetchDataList() {
try {
const urlArray = await db.generateUrlArray({}, { _id: 0 })
return res.send(urlArray)
} catch (ex) {
console.log(`fetchDataList error: ${ex}`)
}
})()
}
..db/index.js
const { List } = require('./models/List')
const generateUrlArray = (query, projection) => {
const dataFromDB = List.find(query, projection).select('symbol')
return linkArray = dataFromDB.map(item => {
return link = `https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=${item.symbol}&apikey=6BUYSS9QR8Y9HH15`
})
}
module.exports = { generateUrlArray }
.models/List.js
const mongoose = require('mongoose')
mongoose.Promise = global.Promise
const ParentSchemaSymbolList = new mongoose.Schema({
symbol: String
})
module.exports.List = mongoose.model('List', ParentSchemaSymbolList)
const generateUrlArray = async (query, projection) => {
const dataFromDB = await List.find(query, projection).select('symbol')
const linkArray = dataFromDB.map(item => {
return link = `https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=${item.symbol}&apikey=6BUYSS9QR8Y9HH15`
})
return linkArray
}