Combine two very similar functions that contain distinct differences - javascript

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.

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;
}
};

Serverless lambda with websocket doesn't save the received data

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?

how to avoid nested subscribe more than two levels

I have four subscribes that dependable on each other. I know there are too many answers regarding avoiding nested subscribe. But nobody answers more than two levels.
how to avoid that many nested subscribe
this is my component code
if (this.code) {
this.appService.updateData('Users/clever_token', {code: this.code}).subscribe(data => {
if (data.detail) {
this.accessToken = data.detail.access_token;
this.appService.getCleverData('/v2.1/me', this.accessToken).subscribe(data1 => {
if (data1.links) {
this.userData.user_type = data1.type;
this.appService.getCleverData(data1.links[1].uri, this.accessToken).subscribe(data2 => {
if (data2.data) {
this.userData.name = data2.data.name.first + ' ' + data2.data.name.last;
this.userData.clever_id = data2.data.id;
this.userData.email = data2.data.email;
this.appService.updateData('Users/cleaver_login', this.userData).subscribe(data3 => {
if (data3.detail) {
console.log(data3.detail);
}
});
}
});
}
});
}
});
}
this is service code
getCleverData(url, token) {
let reqHeader = new HttpHeaders({
'Authorization': 'Bearer ' + token
})
return this.http.get(API_PREFIX + url, { headers: reqHeader })
.pipe(
map((data: any) => {
console.log(data);
if (data) return data;
}),
catchError(this.handleError)
);
}
/** PUT: update a data to the server */
updateData (url, data?) {
let httpParams = new HttpParams();
Object.keys(data).forEach(function (key) {
httpParams = httpParams.append(key, data[key]);
});
return this.http.post(this.apiUrl + url, httpParams, httpOptions)
.pipe(
map((data: any) => {
if (data.status == 0) {
this.presentToast(data.message);
}
if (data) return data;
}),
catchError(this.handleError)
);
}
is there any way to avoid that many subscribe. I can't remove any of it because its some of it from our server and others are third party
There are a few options but as I see you need that structure because you need data from the previous observable so you can use filter() and switchMap().
filter() is able to filter out values which do not contain necessary values switchMap() - to switch to a new stream.
UPDATE:
There is a refactored version of your code:
if (!this.code) {
return;
}
this.appService.updateData('Users/clever_token', {code: this.code}).pipe(
filter(data => !!data.detail),
switchMap(data => {
this.accessToken = data.detail.access_token;
return this.appService.getCleverData('/v2.1/me', this.accessToken);
}),
filter(data1 => !!data1.links),
switchMap(data1 => {
this.userData.user_type = data1.type;
return this.appService.getCleverData(data1.links[1].uri, this.accessToken);
}),
filter(data2 => !!data2.data),
switchMap(data1 => {
this.userData.name = data2.data.name.first + ' ' + data2.data.name.last;
this.userData.clever_id = data2.data.id;
this.userData.email = data2.data.email;
return this.appService.updateData('Users/cleaver_login', this.userData);
}),
).subscribe(data3 => {
if (data3.detail) {
console.log(data3.detail);
}
}

React-native-fbsdk ShareDialog. How to share with prefilled message and photos content together?

I have react-native 0.44.0 and react-native-fbsdk 0.5.0. ShareDialog component work fine, but due to lack of docs explanation had been totally stuck. I have app with own API. I make API call fetch sharing template with photos array.
.then((responseData) => {
console.log("Facebook Share Api Test")
console.log(responseData)
// After receiving result checking Platform
// If this is iOS we should let our result image links be fetched to encode it in Base64.
if(Platform.OS !== 'android'){
console.log("Not Andro!d!")
let imgUrl
let sharePhotoContent
let iteratePhotos = function (data) {
var photoInfo = [];
var ready = Promise.resolve(null)
data.forEach(function (value, i) {
let iconURL = API.SERVER_URL + API.SERVICE_PORT + API.HEAD_ICON_RES_URL + value.photo_id + 'S'
ready = ready.then(function () {
return RNFetchBlob
.fetch('GET', iconURL)
.then(res => res.data)
.then(resData => {
imgUrl = 'data:image/jpeg;base64,' + resData
console.log(imgUrl)
return imgUrl
})
.then(img => {
console.log(img)
let res = {
imageUrl: img,
userGenerated: true,
caption: value.comment
}
return res
})
.catch(err => {
console.log(err)
})
}).then(function (resData) {
photoInfo[i] = resData;
});
});
return ready.then(function () { return photoInfo; });
}
iteratePhotos(responseData.photos).then((res) => {
console.log('res', res)
if(res.length > 0){
sharePhotoContent = {
contentType: 'photo',
contentDescription: 'Wow, check out this great site!',
photos: res
}
} else {
sharePhotoContent = {
contentType: 'link',
contentUrl: 'some url',
message: responseData.message
}
}
ShareDialog.canShow(sharePhotoContent)
.then((canShow) => {
if (canShow) {
return ShareDialog.show(sharePhotoContent);
}
})
.then((result) => {
this.setState({isshowIndicator: false})
if(!result.isCancelled){
this.setState({isFacebookShared: true})
setTimeout(() => alert("Success!"), 100)
}
})
.catch(error => {
this.setState({isshowIndicator: false})
console.log(error)
setTimeout(() => alert('Share fail with error: ' + error), 100)
}
)
})
} else {
let photoInfo = responseData.photos.map(value => {
return {
imageUrl: API.SERVER_URL + API.SERVICE_PORT + API.HEAD_ICON_RES_URL + value.photo_id + 'S',
...value
}
})
console.log(photoInfo, "It IS ANDROID")
if(responseData.photos.length > 0){
var sharePhotoContent = {
contentType: 'photo',
photos: photoInfo
}
} else {
var sharePhotoContent = {
contentType: 'link',
contentUrl: 'some url',
message: responseData.message
}
}
ShareDialog.canShow(sharePhotoContent)
.then((canShow) => {
if (canShow) {
return ShareDialog.show(sharePhotoContent);
}
})
.then((result) => {
this.setState({isshowIndicator: false})
if(!result.isCancelled){
this.setState({isFacebookShared: true})
setTimeout(() => alert("Success!"), 100)
}
})
.catch(error => {
this.setState({isshowIndicator: false})
setTimeout(() => alert('Share fail with error: ' + error), 100)
})
}
})
When I tap share, sharedialog opens and photos that I want are pasted but message line waits to be filled
But I need into ShareDialog opened:
Photos needed to be attached;
Message to be prefilled according that one I received from my API.
Is this possible? Please help this is prerelease feature needed to be implemented very fast and I havent any idea how((
Attaching screenshots that describes 1. what is going now here? 2. What i want to do.
some social network like facebook does not support pre-filling the message for users as seen in their Policy: https://developers.facebook.com/policy/#socialplugins

Firebase deploy - count items in db and assign it

I'm making an iOS app and I have this problem now.
I'd like to count the number of unread messages in database and assign it in a database different closure. Like below.
exports.arrivalNotifications = functions.database.ref('/trips/{tripId}')
.onCreate((snap, context) => {
const data = snap.val();
const uid = data.uid;
var counter = 0
admin.database().ref('/messages/').on('value', function(snapshot) {
snapshot.forEach(function(childSnapshot) {
var childData = childSnapshot.val();
if (childData.read === false) {
counter += 1
}
});
})
return admin.database().ref('/users/' + uid).once('value', snapshot => {
const data = snapshot.val();
const username = data.username
var payload = {
notification: {
title: username ' has ' + counter + " unread message.",
body: 'Press for more'
}
}
admin.messaging().sendToDevice(toUser.fcmToken, payload)
.then(function(response) {
console.log("Successfully sent message:", response);
return null;
})
.catch(function(error) {
console.log("Error sending message:", error);
});
})
})
So I want to use the counter in the payload but I can't find the way to do it. I'm not familiar with JavaScript so if someone can help me I'd appreciate.
I would write your Cloud Function as follow. Please note that I could not test it and it may need some fine-tuning/debugging... especially since it implies chaining several promises.
exports.arrivalNotifications = functions.database.ref('/trips/{tripId}').onCreate((snap, context) => {
const data = snap.val();
const uid = data.uid;
let counter = 0;
return admin.database().ref('/messages/').once('value')
.then(snapshot => {
snapshot.forEach(function (childSnapshot) {
const childData = childSnapshot.val();
if (childData.read === false) {
counter += 1;
}
});
return admin.database().ref('/users/' + uid).once('value');
})
.then(snapshot => {
const data = snapshot.val();
const username = data.username;
const payload = {
notification: {
title: username + ' has ' + counter + ' unread message.',
body: 'Press for more'
}
};
return admin.messaging().sendToDevice(toUser.fcmToken, payload);
})
.then(response => {
console.log("Successfully sent message:", response);
return null;
})
.catch(error => {
console.log("Error sending message:", error);
});
});

Categories

Resources