Serverless lambda with websocket doesn't save the received data - javascript

I'm trying to access one of my node.js lambda functions from an HTML form, using Javascript, but the lambda doesn't save the data.
Here is the Javascript from the HTML page:
let user = document.getElementById('userLogged');
let currentUser = user.textContent;
let channel = document.getElementById('channelLogged');
let currentChannel = channel.textContent;
let message = $("#messageText").val();
let socket = new WebSocket(WEBS + currentChannel);
socket.onopen = () => {
socket.send(JSON.stringify({
action: "sendMessage",
data: {
messageText: message,
username: currentUser,
currentChannel: currentChannel
}
}));
}
});
And here are my lamba function, that is supposed to send and save the messages:
module.exports.sendMessageHandler = (event, context, callback) => {
sendMessageToAllConnected(event).then(() => {
callback(null, successfullResponse)
}).catch (err => {
callback(null, JSON.stringify(err));
});
};
const sendMessageToAllConnected = (event) => {
const body = JSON.parse(event.body);
const message = body.data.messageText;
const channel = body.data.currentChannel;
const user = body.data.username;
return getConnectionIds(channel).then(connectionData => {
return connectionData.Items.map(connectionId => {
return saveMessages.save(event, user, channel, message, connectionId.connectionId);
});
});
};
const getConnectionIds = channel => {
const params = {
TableName: CHATCONNECTION_TABLE,
Key: {
channel: channel
},
ProjectionExpression: 'connectionId'
};
return dynamo.scan(params).promise();
};
module.exports.getMessagesHandler = event => {
const channel = event.queryStringParameters.channel;
const params = {
TableName: MESSAGE_TABLE,
Key: {
channel: channel
},
ProjectionExpression: 'username, messageDate, messageText'
};
return dynamo.scan(params).promise();
};
module.exports.save = (event, user, channel, message, connectionId) => {
const body = JSON.parse(event.body);
const postData = body.data;
const endpoint = event.requestContext.domainName + "/" + event.requestContext.stage;
const apigwManagementApi = new AWS.ApiGatewayManagementApi({
apiVersion: "2018-11-29",
endpoint: endpoint
});
const postParams = {
connectionId : connectionId,
Data: postData
};
const dbParams = {
TableName: MESSAGE_TABLE,
Item: {
channel: channel,
messageDate: Date.now(),
username: user,
messageText: message,
}
};
dynamo.put(dbParams);
return apigwManagementApi.postToConnection(postParams).promise();
};
The sls logs for the sendMessageHandler don't return any error, but I don't see there any result from the request. Also, I have another lambda, that is supposed go get all the saved messages, but returns error that channel key is undefined.
module.exports.getMessagesHandler = event => {
const channel = event.queryStringParameters.channel;
const params = {
TableName: MESSAGE_TABLE,
Key: {
channel: channel
},
ProjectionExpression: 'username, messageDate, messageText'
};
return dynamo.scan(params).promise();
};
Any ideas?

I resolved one of the problems- changed the const channel = event.queryStringParameters.channel; to const channel = event.query.channel; and now I don't get the error that channel is undefined. But now I have a problem with appending the result of getMessagesHandler to the HTML. Here is the code for that.
debugger;
let user = $("#username").val();
let channel = $("#channel").val();
const URL = 'https://ktwdb7v696.execute-api.us-east-1.amazonaws.com/dev/getMessages?channel=';
let realURL = URL + channel;
$.ajax({
type: 'GET',
url: realURL,
success: function (data) {
$("#loginForm").css("visibility", "hidden");
$("#messageForm").css("visibility", "visible");
$("#messages").css("visibility", "visible");
// $("#userLogged").text(user);
// $("#channelLogged").text(channel);
document.getElementById('userLogged').innerHTML = user;
document.getElementById('channelLogged').innerHTML = channel;
document.getElementById('title').innerHTML = 'Please write a message to send';
data.Items.forEach(function (message) {
console.log(message.messageDate);
console.log(message.username);
console.log(message.messageText);
$('#messages').append('<p>Date: ' + message.messageDate + '</p>'
+ '<p>User: ' + message.username + '</p>'
+ '<p>Message: ' + message.messageText + '</p>'
);
});
}
});
});
In fact I think there is nothing to append at all, anyone can help with that?

Related

Firestore : why using serverTimestamp gives different results?

I am having a hard time understanding serverTimestamp in firestore.
When I save a document in database in a firebase function using Fieldvalue.serverTimestamp() or in a javascript client code using serverTimestamp() it sometimes doesn't save the same thing in the database.
See screenshots below :
Sometime I get an object with {nanoseconds: xxx, seconds: xxx} and sometimes I get a timestamp formatted date...
The problem is when I try to query my orders using query(collectionRefOrders, orderBy('createdAt', 'desc'), limit(10)).
The orders with the object appears before the others ones even if they are created after...
Any clue why this happens ? What am I doing wrong ?
Thanks a lot.
EDIT :
Here is the code I use to add documents in the my firebase function (it is a request function I call in a website) :
const { getFirestore, FieldValue } = require('firebase-admin/firestore');
const firebaseDB = getFirestore();
exports.createOrderFromTunnel = functions.region('europe-west3')
.runWith({
timeoutSeconds: 10,
memory: "4GB",
})
.https
.onRequest(async (req, res) => {
cors(req, res, async () => {
try {
const { apiKey } = req.body;
const project = await getProjectFromApiKey(apiKey);
if (!project) {
return res.json({
success: false,
error: 'Unauthorized: invalid or missing api key'
});
}
const contactData = {
address: {},
createdAt: FieldValue.serverTimestamp()
};
const orderData = {
accounting: {
totalHT: 0,
totalTTC: 0,
totalTVA: 0,
},
createdAt: FieldValue.serverTimestamp(),
status: 'NEW',
};
const refProject = firebaseDB
.collection('projects')
.doc(project.id);
const colOrder = firebaseDB.collection(`projects/${project.id}/orders`)
const refOrder = colOrder.doc();
const colContact = firebaseDB.collection(`projects/${project.id}/contacts`)
const refContact = colContact.doc();
await firebaseDB.runTransaction(async transaction => {
const snapProject = await transaction.get(refProject);
const dataProject = snapProject.data();
const sequenceContact = dataProject.sequenceContact;
const sequenceOrder = dataProject.sequenceOrder;
contactData.sequence = sequenceContact;
orderData.sequenceNumber = sequenceOrder;
await transaction.set(refContact, contactData);
orderData.customer.id = refContact.id;
orderData.customer.sequence = sequenceContact;
await transaction.set(refOrder, orderData);
await transaction.update(refProject, {
sequenceContact: sequenceContact + 1,
sequenceOrder: sequenceOrder + 1,
totalContacts: dataProject.totalContacts + 1,
totalOrders: dataProject.totalOrders + 1,
});
return refOrder.id;
});
return res.json({
success: true
});
} catch (err) {
functions.logger.error(err);
return res.json({
success: false,
err
});
}
});
});
Here is the code I use to add documents in my client code (it is a web app in javascript) :
const createOrder = async (projectId) => {
try {
const orderData = {
accounting: {
totalHT: 0,
totalTTC: 0,
totalTVA: 0,
},
createdAt: serverTimestamp(),
status: 'NEW',
surface: 0,
};
const refProject = doc(firebaseDB, 'projects', projectId);
const colOrder = collection(firebaseDB, `projects/${projectId}/orders`)
const refOrder = doc(colOrder);
return await runTransaction(firebaseDB, async (transaction) => {
const snapProject = await transaction.get(refProject);
if (!snapProject.exists()) {
throw "Document does not exist!";
}
const dataProject = snapProject.data();
const sequence = dataProject.sequenceOrder;
orderData.sequenceNumber = sequence;
transaction.set(refOrder, orderData);
transaction.update(refProject, { sequenceOrder: sequence + 1, totalOrders: dataProject.totalOrders + 1 });
return refOrder.id;
});
} catch (e) {
console.error(e);
return null;
}
};

2 Jquery ajax post requests. 2nd one gives error: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

I have one GET request at first to retrieve a data structure. Then I perform some changes and perform a PUT request to send the data structure to the server and update the record. When I click the submit button on the on the first form it works as expected and when I submit the second one it executes as expected, but it logs 2 errors. Furthermore, when I make 2 edits successively it crashes the app and gives an RangeError: Maximum call stack size exceeded error. The code related to the RangeError: Maximum call stack size exceeded error is at the bottom.
Image showing initial errors on update
Range Error Console Log
Here is my client side script:
<script>
const capitalize = ([first, ...rest]) =>
${first.toUpperCase()}${rest.join('')};
let patientDataRetrieved = false;
let _xhr,_xhr2 = null;
let patientID ;
let first_name;
let last_name;
let email;
let sex;
let date_of_birth;
let patient_phone;
let marital_status;
let emergency_contact1_rel ;
let emergency_contact1_phone;
let emergency_contact2_rel ;
let emergency_contact2_phone;
let spouse_phone;
$("body").on("contextmenu",function(e){
return false;
});
$('body').bind('cut copy ', function (e) {
e.preventDefault();
});
document
.getElementById("search")
.addEventListener("submit",ev => {
ev.preventDefault();
patientID = ev.target.patientID.value;
let uid = patientID;
getPatientDetails()
function getPatientDetails(){
if(_xhr2!= null)
_xhr2.abort();
if(patientDataRetrieved===false){
$(document).ready(()=>{
_xhr = $.ajax({
url: '/profile/'+uid,
type: 'GET',
cache: false,
success: function(data){
//Instantiate Variables of User Data
let userdata = data;
console.log(userdata);
first_name = userdata.first_name;
last_name = userdata.last_name;
sex = userdata.sex;
email = userdata.email;
let blood_type = userdata.blood_type;
date_of_birth = userdata.date_of_birth;
spouse_phone = userdata.spouse_phone;
patient_phone = userdata.patient_phone;
marital_status =userdata.marital_status;
spouse_phone = userdata.spouse_phone;
emergency_contact1_rel = userdata.emergency.contact1.relationship;
emergency_contact1_phone = userdata.emergency.contact1.phone;
emergency_contact2_rel = userdata.emergency.contact2.relationship;
emergency_contact2_phone = userdata.emergency.contact2.phone;
let chronic_illnesses = userdata.chronic_illnesses;
let past_surgeries = userdata.past_surgeries;
let allergies = userdata.allergies;
let long_term_medication = userdata.long_term_medication;
//Append User Data to respective td in table
$(".firstname").append(capitalize(first_name));
$(".lastname").append(capitalize(last_name));
$("#blood_type").val(blood_type);
$(".sex").append(capitalize(sex));
$(".date_of_birth").append(date_of_birth);
$(".patient_phone").append(patient_phone);
$(".marital_status").append(capitalize(marital_status));
$(".spouse_phone").append(spouse_phone);
$(".emergency_contact1_rel").append(capitalize(emergency_contact1_rel));
$(".emergency_contact1_rel").append(", Phone: "+emergency_contact1_phone);
$(".emergency_contact2_rel").append(capitalize(emergency_contact2_rel));
$(".emergency_contact2_rel").append(", Phone: "+emergency_contact2_phone);
$("#chronic_illnesses").val(chronic_illnesses);
$("#past_surgeries").val(past_surgeries);
$("#allergies").val(allergies);
$("#long_term_medication").val(long_term_medication);
document.getElementById("patientData").style.display ="block";
patientDataRetrieved = true;
return true;
}
, error: function(jqXHR, textStatus, err){
alert('text status '+textStatus+', err
'+err)
}
});
});
}
}
});
document.getElementById("editForm").addEventListener("submit", ev =>{
ev.preventDefault();
updatePatientInfo()
function updatePatientInfo() {
if(_xhr!=null){_xhr.abort()}
let formData = {
first_name: first_name,
last_name: last_name,
email: email,
blood_type: $("#blood_type").val(),
sex: sex,
date_of_birth:date_of_birth,
patient_phone:patient_phone,
marital_status:marital_status,
spouse_phone: spouse_phone,
emergency_contact1_relationship:emergency_contact1_rel,
emergency_contact1_phone:emergency_contact1_phone,
emergency_contact2_relationship:emergency_contact2_rel,
emergency_contact2_phone: emergency_contact2_phone,
chronic_illnesses: $("#chronic_illnesses").val(),
past_surgeries: $("#past_surgeries").val(),
allergies: $("#allergies").val(),
long_term_medication: $("#long_term_medication").val(),
uid: patientID,
};
_xhr2 = $.ajax({
url: "/edit_record_process",
type: "PUT",
data: formData,
datatype:"text",
encoded:true,
cache: false,
success:function(data){
if(data){
window.location.reload()
}
},
error: (err)=>{
alert(err);
}
});
}
});
Here's the server side code:
app.get("/profile/:uid", function (req, res){
//const current_user =
//const current_user =
const uid = req.params.uid;
db.ref("user_file_ref").on("value", snapshot=>{
snapshot.forEach( dataSnapshot=>{
if(uid === dataSnapshot.val().user_id){
//line referenced in range error
getFileFromNet(dataSnapshot.val().ipfs_ref).then( result =>
{
const hash = JSON.parse(result);
let decrypted_string = decrypt(hash);
let return_data = JSON.parse(decrypted_string);
return res.send(return_data);
}).catch((err) =>{
return console.log(err);
});
}else{
}
})
})
return true;
});
app.put("/edit_record_process", (req,res)=>{
let first_name = req.body.first_name;
let last_name= req.body.last_name;
let blood_type = req.body.blood_type ;
let sex = req.body.sex ;
let date_of_birth= req.body.date_of_birth ;
let email = req.body.email ;
let patient_phone= req.body.patient_phone ;
let userID = req.body.uid;
let marital_status= req.body.marital_status ;
let emergency= {
contact1:
{
"relationship":req.body.emergency_contact1_relationship,
"phone": req.body.emergency_contact1_phone
},
contact2:
{
"relationship":req.body.emergency_contact2_relationship,
"phone": req.body.emergency_contact2_phone
}
};
let chronic_illnesses = req.body.chronic_illnesses ;
let past_surgeries = req.body.past_surgeries ;
let allergies = req.body.allergies ;
let long_term_medication = req.body.long_term_medication ;
const userdata = {
"first_name" : first_name,
"last_name" : last_name,
"sex": sex,
"blood_type": blood_type,
"date_of_birth":date_of_birth,
"email":email,
"patient_phone": patient_phone,
"marital_status": marital_status,
"emergency": emergency,
"chronic_illnesses": chronic_illnesses,
"past_surgeries":past_surgeries,
"allergies": allergies,
"long_term_medication": long_term_medication,
}
let userdata_string = JSON.stringify(userdata);
let encrypted_user_data = JSON.stringify(encrypt(userdata_string));
sendFileToNet(encrypted_user_data).then((ipfs_object)=>{
let ipfs_path = ipfs_object.path;
db.ref("user_file_ref").on("value", snapshot=>{
snapshot.forEach(dataSnapshot => {
if(userID === dataSnapshot.val().user_id){
dataSnapshot.ref.update({ipfs_ref:ipfs_path}).then(()=>
{
return res.sendStatus(201);
}).catch((err)=>{
console.log(err)
})
}
})
})
});
});
IPFS API code:
const sendFileToNet = async (file_data) => {
const cid = function (data) {
return client.add(data).then(cid =>{return cid;}).catch(err=>{
console.log(err)})
}
const real_cid = await cid(file_data);
return real_cid;
}
const getFileFromNet = async (cid) =>{
const stream = client.cat(cid)
let data = ''
for await (const chunk of stream) {
// chunks of data are returned as a Buffer, convert it back to a
string
data += chunk.toString()
}
return data;
}

Firebase cloud messaging sendToDevice works properly but sendMulticast fails for the same list of tokens

For certain types of messages, I want to target users by FIRTokens vs topic, which are stored in my real-time database. I load these tokens with async/await and then decide if I want to send notifications to a topic vs a smaller list of users. The data loading code works as expected. But what's odd is that if I use .sendMulticast(payload), the notifications fail for all tokens in the list. On the other hand if I use .sendToDevice(adminFIRTokens, payload) the notification goes successfully to all my users. Right now my list has 2 tokens and with sendMulticast I have 2 failures and with sendToDevice I have 2 successes. Am I missing the point of what sendMulticast is supposed to do? According to the docs: Send messages to multiple devices:
The REST API and the Admin FCM APIs allow you to multicast a message to a list of device registration tokens. You can specify up to 500 device registration tokens per invocation.
So both should logically work. Then why does one fail and the other work? In fact with sendToDevice I get a multicastId in the response!
Here are some console outputs:
sendToDevice:
Sent filtered message notification successfully:
{
results:
[
{ messageId: '0:1...45' },
{ messageId: '16...55' }
],
canonicalRegistrationTokenCount: 0,
failureCount: 0,
successCount: 2,
multicastId: 3008...7000
}
sendMulticast:
List of tokens that caused failures: dJP03n-RC_Y:...MvPkTbuV,fDo1S8jPbCM:...2YETyXef
Cloud function to send the notification:
functions.database
.ref("/discussionMessages/{autoId}/")
.onCreate(async (snapshot, context) => {
// console.log("Snapshot: ", snapshot);
try {
const groupsRef = admin.database().ref("people/groups");
const adminUsersRef = groupsRef.child("admin");
const filteredUsersRef = groupsRef.child("filtered");
const filteredUsersSnapshot = await filteredUsersRef.once("value");
const adminUsersSnapshot = await adminUsersRef.once("value");
var adminUsersFIRTokens = {};
var filteredUsersFIRTokens = {};
if (filteredUsersSnapshot.exists()) {
filteredUsersFIRTokens = filteredUsersSnapshot.val();
}
if (adminUsersSnapshot.exists()) {
adminUsersFIRTokens = adminUsersSnapshot.val();
}
const topicName = "SpeechDrillDiscussions";
const message = snapshot.val();
const senderName = message.userName;
const senderCountry = message.userCountryEmoji;
const title = senderName + " " + senderCountry;
const messageText = message.message;
const messageTimestamp = message.messageTimestamp.toString();
const messageID = message.hasOwnProperty("messageID")
? message.messageID
: undefined;
const senderEmailId = message.userEmailAddress;
const senderUserName = getUserNameFromEmail(senderEmailId);
const isSenderFiltered = filteredUsersFIRTokens.hasOwnProperty(
senderUserName
);
var payload = {
notification: {
title: title,
body: messageText,
sound: "default",
},
data: {
messageID: messageID,
messageTimestamp: messageTimestamp,
},
};
if (isSenderFiltered) {
adminFIRTokens = Object.values(adminUsersFIRTokens);
// payload.tokens = adminFIRTokens; //Needed for sendMulticast
return (
admin
.messaging()
.sendToDevice(adminFIRTokens, payload)
// .sendMulticast(payload)
.then(function (response) {
if (response.failureCount === 0) {
console.log(
"Sent filtered message notification successfully:",
response
);
} else {
console.log(
"Sending filtered message notification failed for some tokens:",
response
);
}
// if (response.failureCount > 0) {
// const failedTokens = [];
// response.responses.forEach((resp, idx) => {
// if (!resp.success) {
// failedTokens.push(adminFIRTokens[idx]);
// }
// });
// console.log(
// "List of tokens that caused failures: " + failedTokens
// );
// }
return true;
})
);
} else {
payload.topic = topicName;
return admin
.messaging()
.send(payload)
.then(function (response) {
console.log("Notification sent successfully:", response);
return true;
});
}
} catch (error) {
console.log("Notification sent failed:", error);
return false;
}
});
I think it's an issue of using a different payload structure.
This is the old one (without iOS specific info):
var payload = {
notification: {
title: title,
body: messageText,
sound: "default",
},
data: {
messageID: messageID,
messageTimestamp: messageTimestamp,
},
};
Whereas this is the new version (apns has iOS specific info)
var payload = {
notification: {
title: title,
body: messageText,
},
data: {
messageID: messageID,
messageTimestamp: messageTimestamp,
},
apns: {
payload: {
aps: {
sound: "default",
},
},
},
};
With the new structure, both send and sendMulticast are working properly. Which would fail to send or give errors like apns key is not supported in payload.
The new function:
functions.database
.ref("/discussionMessages/{autoId}/")
.onCreate(async (snapshot, context) => {
// console.log("Snapshot: ", snapshot);
try {
const groupsRef = admin.database().ref("people/groups");
const adminUsersRef = groupsRef.child("admin");
const filteredUsersRef = groupsRef.child("filtered");
const filteredUsersSnapshot = await filteredUsersRef.once("value");
const adminUsersSnapshot = await adminUsersRef.once("value");
var adminUsersFIRTokens = {};
var filteredUsersFIRTokens = {};
if (filteredUsersSnapshot.exists()) {
filteredUsersFIRTokens = filteredUsersSnapshot.val();
}
if (adminUsersSnapshot.exists()) {
adminUsersFIRTokens = adminUsersSnapshot.val();
}
// console.log(
// "Admin and Filtered Users: ",
// adminUsersFIRTokens,
// " ",
// filteredUsersFIRTokens
// );
const topicName = "SpeechDrillDiscussions";
const message = snapshot.val();
// console.log("Received new message: ", message);
const senderName = message.userName;
const senderCountry = message.userCountryEmoji;
const title = senderName + " " + senderCountry;
const messageText = message.message;
const messageTimestamp = message.messageTimestamp.toString();
const messageID = message.hasOwnProperty("messageID")
? message.messageID
: undefined;
const senderEmailId = message.userEmailAddress;
const senderUserName = getUserNameFromEmail(senderEmailId);
const isSenderFiltered = filteredUsersFIRTokens.hasOwnProperty(
senderUserName
);
console.log(
"Will attempt to send notification for message with message id: ",
messageID
);
var payload = {
notification: {
title: title,
body: messageText,
},
data: {
messageID: messageID,
messageTimestamp: messageTimestamp,
},
apns: {
payload: {
aps: {
sound: "default",
},
},
},
};
console.log("Is sender filtered? ", isSenderFiltered);
if (isSenderFiltered) {
adminFIRTokens = Object.values(adminUsersFIRTokens);
console.log("Sending filtered notification with sendMulticast()");
payload.tokens = adminFIRTokens; //Needed for sendMulticast
return admin
.messaging()
.sendMulticast(payload)
.then((response) => {
console.log(
"Sent filtered message (using sendMulticast) notification: ",
JSON.stringify(response)
);
if (response.failureCount > 0) {
const failedTokens = [];
response.responses.forEach((resp, idx) => {
if (!resp.success) {
failedTokens.push(adminFIRTokens[idx]);
}
});
console.log(
"List of tokens that caused failures: " + failedTokens
);
}
return true;
});
} else {
console.log("Sending topic message with send()");
payload.topic = topicName;
return admin
.messaging()
.send(payload)
.then((response) => {
console.log(
"Sent topic message (using send) notification: ",
JSON.stringify(response)
);
return true;
});
}
} catch (error) {
console.log("Notification sent failed:", error);
return false;
}
});

Keeps calling itself when using the onClick event handler

what I expect to happen:
when the user clicks addProject button the event listener will run calling
formSubmit
and we will check for the date if it's valid or not then if it's valid it will call fetchingCompanyNameAndUserData
it will fetch the required data update the state and it will call checkUniqueName which again will fetch some data making sure there is no duplication and then it's supposed to call this.insert()
which will finally insert the data into our firestore-NoSQL- DB.
The issue:
these function specially the checkUniqueName keeps calling it self over and over and I don't know what is wrong there.
the code:
formSubmit = event => {
event.preventDefault();
const isLoading = this.state;
var sdate = this.state.projectData.sdate;
var edate = this.state.projectData.edate;
if (sdate > edate) {
NotificationManager.error(`Please entre a valid dates`);
return;
} else {
// isLoading = true;
this.fetchingCompanyNameAndUserData();
}
};
fetchingCompanyNameAndUserData = async () => {
const userRef = fireStore.collection('users');
const userData = await userRef.where("Email", "==", auth.currentUser.email).get();
userData.forEach(doc => {
console.log('this one must match', doc.data().CompanyName)
const cashedFirstName = doc.data().FirstName;
const cashedLastName = doc.data().LastName;
const fullName = cashedFirstName + ' ' + cashedLastName;
return this.setState({
companyName: doc.data().CompanyName,
userName: fullName,
}, () => {
console.log('done fetching');
this.checkUniqueName();
});
})
};
checkUniqueName = async () => {
const projectName = this.state.projectData.title;
const companyName = this.state.companyName;
const projectRef = fireStore.collection('PROJECT')
const projectData = await projectRef.where("ProjectName", "==", projectName).get();
projectData.forEach(doc => {
if (doc.data().CompanyName !== companyName) {
console.log('checking unique nameing');
this.insert();
} else {
NotificationManager.error('this project already exists');
}
})
}
async insert() {
//async function foo() {
console.log('insreting proooo');
var ptitle = this.state.projectData.title;
var pdesc = this.state.projectData.desc;
var sdate = this.state.projectData.sdate;
var edate = this.state.projectData.edate;
var status = this.state.projectData.status;
var companyName = this.state.companyName;
try {
let response = await fireStore.collection("PROJECT").add(
{
ProjectName: ptitle,
CompanyName: companyName,
ProjectDescription: pdesc,
startDate: sdate,
EndDate: edate,
Status: status,
CreatedBy: auth.currentUser.email,
CreatedDate: toUTCDateString(new Date()),
LastModifiedBy: auth.currentUser.email,
LastModifiedDate: toUTCDateString(new Date()),
UserDocId: auth.currentUser.uid
});
let doc = await fireStore.collection("PROJECT").doc(response.id).get()
this.handleClose();
//alert(doc.id)
var d1 = doc.id;
this.props.history.push('/app/dashboard/addproject/' + d1);
//this.handleClose;
NotificationManager.success('Project Created Successfully!');
}
catch (error) {
//console.log('error: ', error);
console.log(error)
}
}
Hope I made it as clear as possible here
Your checkUniqueName() function can be rewritten as:
checkUniqueName = async () => {
const projectName = this.state.projectData.title;
const companyName = this.state.companyName;
const projectRef = fireStore.collection('PROJECT')
const qsMatchingProjects = await projectRef.where("ProjectName", "==", projectName).where("CompanyName", "==", companyName).get();
if (qsMatchingProjects.empty) {
this.insert();
} else {
NotificationManager.error('this project already exists');
}
}

Combine two very similar functions that contain distinct differences

I have two functions in my Node.js application:
retrieveIssues: function(githubAppId, pemFilePath, repoOrg, repoName, callback) {
const octokit = require('#octokit/rest')();
let data = null;
gitInstallationAccessToken.genInstallationAccessToken(githubAppId, pemFilePath, (installationAccessToken) => {
octokit.authenticate({
type: 'app',
token: `${installationAccessToken}`
});
async function paginate(method) {
let response = await method({
q: "repo:" + repoOrg + "/" + repoName + " is:issue" + " state:open",
per_page: 100
});
data = response.data.items;
var count = 0;
while (octokit.hasNextPage(response)) {
count++;
console.log(`request n°${count}`);
response = await octokit.getNextPage(response);
data = data.concat(response.data.items);
}
return data;
}
paginate(octokit.search.issues)
.then(data => {
callback(data);
})
.catch(error => {
console.log(error);
});
});
}
retrieveEnerpriseIssues: function(repoOrg, repoName, callback) {
const octokit = require('#octokit/rest')({
baseUrl: config.githubEnterprise.baseUrl
});
let data = null;
// token auth
octokit.authenticate({
type: 'basic',
username: config.githubEnterprise.username,
password: config.githubEnterprise.token
});
async function paginate(method) {
let response = await method({
q: "repo:" + repoOrg + "/" + repoName + " is:issue" + " label:sdk" + " state:open",
per_page: 100
});
data = response.data.items;
var count = 0;
while (octokit.hasNextPage(response)) {
count++;
console.log(`request n°${count}`);
response = await octokit.getNextPage(response);
data = data.concat(response.data.items);
}
return data;
}
paginate(octokit.search.issues)
.then(data => {
callback(data);
})
.catch(error => {
console.log(error);
});
}
}
The first accesses public GitHub, the second a private Github. Whilst there are some very distinct differences(authentication type and number of parameters passed etc), they are very similar. I was wondering if these could be refactored into a single function or if that is even a good idea. If it is possible and could improve my code how is this done?
You can, and given the amount of duplication, probably should refactor. It was a little tricky without any tests and without the ability to run the code but maybe this would do the trick?
retrieve: function({repoOrg, repoName, callback, octoKitArgs, octoKitAuthArgs}) {
const octokit = require('#octokit/rest')(octoKitArgs);
let data = null;
octokit.authenticate(octoKitAuthArgs);
async function paginate(method) {
let response = await method({
q: "repo:" + repoOrg + "/" + repoName + " is:issue" + " label:sdk" + " state:open",
per_page: 100
});
data = response.data.items;
var count = 0;
while (octokit.hasNextPage(response)) {
count++;
console.log(`request n°${count}`);
response = await octokit.getNextPage(response);
data = data.concat(response.data.items);
}
return data;
}
paginate(octokit.search.issues)
.then(data => {
callback(data);
})
.catch(error => {
console.log(error);
});
}
// call as private github
retrieve({
repoOrg: "",
reportName: "",
callback: () => {},
octoKitArgs: {baseUrl: config.githubEnterprise.baseUrl},
octoKitAuthArgs: {type: 'basic', username: config.githubEnterprise.username, password: config.githubEnterprise.token},
});
// call as public github
gitInstallationAccessToken.genInstallationAccessToken(githubAppId, pemFilePath, (installationAccessToken) =>
retrieve({
repoOrg: "",
reportName: "",
callback: () => {},
octoKitArgs: undefined,
octoKitAuthArgs: {type: 'app', token: `${installationAccessToken}`},
})
);
Let me know how this looks.

Categories

Resources