EDIT: it seems there is an issue in the code. Would anyone know how to fix this error?
The error I am getting is:
Uncaught SyntaxError: Unexpected token ')'
contentscript.js:103 (anonymous function)
const translatorapikey = "XXX";
const toneanalyzerapikey = "XXX";
function getAPIKeyV2(apikey){
return new Promise(function(resolve, reject){
var xmlRequest = new XMLHttpRequest();
if(window.XMLHttpRequest){
xmlRequest.open("POST", "https://iam.bluemix.net/identity/token")
xmlRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xmlRequest.setRequestHeader("Accept", "application/json")
xmlRequest.send(encodeURI("grant_type=urn:ibm:params:oauth:grant-type:apikey&apikey="+apikey));
xmlRequest.onreadystatechange = function(){
if(xmlRequest.readyState ==4 && xmlRequest.status==200) {
var parsedData = JSON.parse(xmlRequest.responseText)
resolve (parsedData.access_token);
}
}
}
});
}
var callToneAnalyzer = function (word) {
var textContent = String(word.selectionText);
var xhr = new XMLHttpRequest();
var toneanalyzertoken = getAPIKeyV2(toneanalyzerapikey);
toneanalyzertoken.then(function(result){
var inputContent = textContent.replace(/%20/g, "");
xhr.open("GET", "https://api.us-south.tone-analyzer.watson.cloud.ibm.com/api/v3/tone?sentences=true&version=2016-05-19&text="+inputContent)
xhr.setRequestHeader("Authorization", "Bearer "+result);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.setRequestHeader("Accept", "application/json")
xhr.send();
xhr.onreadystatechange = function(){
if(xhr.readyState == 4 && xhr.status==200){
var result1 = xhr.responseText;
var obj = JSON.parse(result1);
var fulltone = obj.document_tone.tone_categories;
var angerTone = obj.document_tone.tone_categories[0].tones[0].tone_name;
var angerScore = obj.document_tone.tone_categories[0].tones[0].score;
var disgustTone = obj.document_tone.tone_categories[0].tones[1].tone_name;
var disgustScore = obj.document_tone.tone_categories[0].tones[1].score;
var fearTone = obj.document_tone.tone_categories[0].tones[2].tone_name;
var fearScore = obj.document_tone.tone_categories[0].tones[2].score;
var joyTone = obj.document_tone.tone_categories[0].tones[3].tone_name;
var joyScore = obj.document_tone.tone_categories[0].tones[3].score;
var sadnessTone = obj.document_tone.tone_categories[0].tones[4].tone_name;
var sadnessScore = obj.document_tone.tone_categories[0].tones[4].score;
alert(angerTone + "= " + angerScore*100 + " %" + ";" + "\n" + disgustTone + "= " + disgustScore*100 + " %" + "\n" + fearTone + "= " + fearScore*100 + " %" + "\n" + joyTone + "= " + joyScore*100 + " %" + "\n" + sadnessTone + "= " + sadnessScore*100 + " %");
}
}
})
};
function Translator(word, lang, langname) {
var textContent = String(word.selectionText);
var accesstoken = getAPIKeyV2(translatorapikey);
accesstoken.then(function(result){
var inputContent = textContent.replace(/%20/g, " ");
var xmlRequest = new XMLHttpRequest();
if(window.XMLHttpRequest){
xmlRequest.open("POST", "https://api.us-south.language-translator.watson.cloud.ibm.com/api/v3/translate?version=2018-05-01")
xmlRequest.setRequestHeader("Authorization", "Bearer "+ result);
xmlRequest.setRequestHeader("Content-type", "application/json");
xmlRequest.setRequestHeader("Accept", "application/json");
var data = {
"text": inputContent,
"source": "en",
"target": String(lang)
}
xmlRequest.send(JSON.stringify(data));
xmlRequest.onreadystatechange = function() {
if(xmlRequest.readyState ==4 && xmlRequest.status==200){
var translatedtext = JSON.parse(xmlRequest.responseText);
alert("Translated to " + langname + "\n" + JSON.stringify(translatedtext.translations));
}
}
})
}
function generalTranslator(word) {
var childname = word.menuItemId;
if (childname == 'child1') {
Translator(word, 'es', 'Spanish');
return;
}
if (childname == 'child2') {
Translator(word, 'ar', 'Arabic');
return;
}
if (childname == 'child3') {
Translator(word, 'fr', 'French');
return;
}
if (childname == 'child4') {
Translator(word, 'pt', 'Portuguese');
return;
}
if (childname == 'child5') {
Translator(word, 'de', 'German');
return;
}
}
chrome.contextMenus.create({
title: "IBM Watson API V1",
id: 'parent',
contexts: ["selection"]
});
chrome.contextMenus.create({
title: "Translate to Spanish",
parentId: "parent",
id: "child1",
contexts: ["selection"],
onclick: generalTranslator
});
chrome.contextMenus.create({
title: "Translate to Arabic",
parentId: "parent",
id: "child2",
contexts: ["selection"],
onclick: generalTranslator
});
chrome.contextMenus.create({
title: "Translate to French",
parentId: "parent",
id: 'child3',
contexts: ["selection"],
onclick: generalTranslator
});
chrome.contextMenus.create({
title: "Translate to Portuguese",
parentId: "parent",
id: 'child4',
contexts: ["selection"],
onclick: generalTranslator
});
chrome.contextMenus.create({
title: "Translate to German",
parentId: "parent",
id: 'child5',
contexts: ["selection"],
onclick: generalTranslator
});
chrome.contextMenus.create({
title: "Tone Analyzer",
parentId: "parent",
id: 'child6',
contexts: ["selection"],
onclick: callToneAnalyzer
});
For context I am not a developer or a software eng. Just a casual person intrigued by IBM Watson's Tone Analyzer. I wanted to build my own Chrome extension that can analyze the tone of the messages I write from my laptop, so I followed all the steps described in IBM's documentation. To my surprise, despite my limited technical knowledge, I managed to go through the steps described in this doc.
After following up to step 5, I see my extension succesfully displayed in chrome://extensions/
However, I can't reproduce Step 6. When I right click, the IBM watson API menu is not displayed.
What am I missing? Would anyone have any pointers?
Related
here i have two scripts one is suitelet other one is client script .
Please forgive me for the silly mistake as I have not much knowledge in netsuite.
/**
*#NApiVersion 2.x
*#NScriptType Suitelet
*/
define(['N/record','N/ui/serverWidget','N/redirect','N/runtime','N/search'],function(record,serverWidget,redirect,runtime,search){
function onRequest(context){
var Request = context.request;
var Response = context.response;
if(Request.method == 'GET') {
var form=serverWidget.createForm({title:"Customer entry Suitelet"});
var primaryinfo=form.addFieldGroup({
label:'Primary Information',
id:'custpage_advs_primary_info',
});
var firstname=form.addField({
label:'First Name',
id:'custpage_advs_first_name',
type:serverWidget.FieldType.TEXT,
container:'custpage_advs_primary_info'
});
firstname.isMandatory=true;
var lastname=form.addField({
label:'Last Name',
id:'custpage_advs_last_name',
type:serverWidget.FieldType.TEXT,
container:'custpage_advs_primary_info'
});
lastname.isMandatory=true;
var email=form.addField({
label:'Email',
id:'custpage_advs_email',
type:serverWidget.FieldType.EMAIL,
container:'custpage_advs_primary_info'
});
email.isMandatory=true;
var phone=form.addField({
label:'Phone',
id:'custpage_advs_phone',
type:serverWidget.FieldType.PHONE,
container:'custpage_advs_primary_info'
});
var mysublist = form.addSublist({
id: 'custpage_advs_sublistid',
type: serverWidget.SublistType.LIST,
label:'My Sublist'
});
mysublist.addField({
id: 'custpage_advs_sublist_internalid',
type: serverWidget.FieldType.INTEGER,
label: 'Internal Id'
});
mysublist.addField({
id: 'custpage_advs_sublist_fullname',
type: serverWidget.FieldType.TEXT,
label: 'Name'
});
mysublist.addField({
id: 'custpage_advs_sublist_fname',
type: serverWidget.FieldType.TEXT,
label: 'First Name'
});
mysublist.addField({
id: 'custpage_advs_sublist_lname',
type: serverWidget.FieldType.TEXT,
label: 'Last Name'
});
mysublist.addField({
id: 'custpage_advs_sublist_email',
type: serverWidget.FieldType.EMAIL,
label: 'Email'
});
mysublist.addField({
id: 'custpage_advs_sublist_phone',
type: serverWidget.FieldType.PHONE,
label: 'Phone'
});
mysublist.addField({
id: 'custpage_advs_sublist_view',
type: serverWidget.FieldType.URL,
label: 'VIEW'
});
var submit=form.addSubmitButton({
id : 'custpage_advs_submit_record',
label : 'Submit'
});
Response.writePage(form);
// form.clientScriptModulePath = './advs_cs_populatesublisttask.js';
form.clientScriptFileId = 91370;
var fnameValue = Request.parameters.custparam_first_name;
var lnameValue = Request.parameters.custparam_last_name;
var emailValue = Request.parameters.custparam_email;
var phoneValue = Request.parameters.custparam_phone;
log.debug("param from clientScript "+fnameValue);
if(fnameValue){
firstname.defaultValue =fnameValue;
// form.clientScriptFileId = 91370;
var fnameValue = Request.parameters.custparam_first_name;
var lnameValue = Request.parameters.custparam_last_name;
var emailValue = Request.parameters.custparam_email;
var phoneValue = Request.parameters.custparam_phone;
var fnamecustomerSearch = search.create({
type:'customrecord_advs_customer_entry_form',
filters:['custrecord_advs_first_name',"startswith",fnameValue],
columns:[
search.createColumn({name: "name"}),
search.createColumn({name: "custrecord_advs_first_name"}),
search.createColumn({name: "custrecord_advs_last_name"}),
search.createColumn({name: "custrecord_advs_email"}),
search.createColumn({name: "custrecord_advs_phone"}),
search.createColumn({name: "internalid"}),
search.createColumn({name: "id"})
]
});
var counter = 0;
fnamecustomerSearch.run().each(function(result) {
log.debug("my result", +result);
var oldfullname = result.getValue('name');
var InternalidVal = result.getValue('internalid');
var firstname=result.getValue('custrecord_advs_first_name');
var lastname=result.getValue('custrecord_advs_last_name');
var email=result.getValue('custrecord_advs_email');
var phone=result.getValue('custrecord_advs_phone');
// var view=result.getValue('view');
var view= result.getValue('edit|view');
log.debug("view is"+view);
mysublist.setSublistValue({
id: 'custpage_advs_sublist_internalid',
line: counter,
value: InternalidVal
});
mysublist.setSublistValue({
id: 'custpage_advs_sublist_fullname',
line: counter,
value:oldfullname
});
mysublist.setSublistValue({
id: 'custpage_advs_sublist_fname',
line: counter,
value: firstname
});
mysublist.setSublistValue({
id: 'custpage_advs_sublist_lname',
line: counter,
value: lastname
});
mysublist.setSublistValue({
id: 'custpage_advs_sublist_email',
line: counter,
value: email
});
mysublist.setSublistValue({
id: 'custpage_advs_sublist_phone',
line: counter,
value:phone
});
counter++;
return true;
});
}
}
return true;
}
else
{
var Fname= Request.parameters.custpage_advs_first_name;
var Lname=Request.parameters.custpage_advs_last_name;
var Email=Request.parameters.custpage_advs_email;
var Phone=Request.parameters.custpage_advs_phone;
var Fullname=Fname+' '+Lname;
var customRecord=record.create({
type:'customrecord_advs_customer_entry_form',
isDynamic:true,
});
customRecord.setValue({
fieldId:'name',
value:Fullname
});
customRecord.setValue({
fieldId:'custrecord_advs_first_name',
value:Fname
});
customRecord.setValue({
fieldId:'custrecord_advs_first_name',
value:Lname
});
customRecord.setValue({
fieldId:'custrecord_advs_email',
value:Email
});
customRecord.setValue({
fieldId:'custrecord_advs_phone',
value:Phone
});
var recordId=customRecord.save({
enableSourcing: false,
ignoreMandatoryFields: false
});
redirect.toSuitelet({
scriptId: 'customscript_advs_ss_customersuitelet',
deploymentId: 'customdeploy_advs_ss_customersuitelet',
});
}
}
return{
onRequest:onRequest
}
});
and then I added some conditions in client scripts .
but all I need is to add an edit/ view column in my suitelet
FYR
here is my client script
/**
* #NApiVersion 2.0
* #NScriptType ClientScript
*/
define(['N/currentRecord','N/search','N/record','N/url'],function(currentRecord,search,record,url){
function fieldChanged(context) {
var recordObj=context.currentRecord;
var name = context.fieldId;
if (name == 'custpage_advs_first_name'){
var fnameValue=recordObj.getValue({
fieldId:'custpage_advs_first_name'
});
alert("First name that you have entered is " +fnameValue);
var suiteUrl = url.resolveScript({
scriptId: 'customscript_advs_ss_populatesublisttask',
deploymentId: 'customdeploy_advs_ss_populatesublisttask',
returnExternalUrl:false,
params : {
custparam_first_name : fnameValue, //define a parameter and pass the value to it
}
});
}
}
else{
return true;
}
setWindowChanged(window,false);
window.location = suiteUrl;
}
return{
fieldChanged:fieldChanged
}
});
I guess I'm mostly just looking for a high level answer, because I'm not sure I'm even approaching this correctly.
You should reduce the code you paste in your question to the parts that isolate the problem.
To solve your issue, add a new field of type INLINEHTML.
sublist.addField({
id: 'YOUR_ID',
label: 'YOUR_LABEL',
type: serverWidget.FieldType.INLINEHTML
})
For each line, set the value to a link using regular html anchor tags.
const view_url = url.resolveRecord({
recordType: TYPE
recordId: ID,
isEditMode: false
})
const edit_url = url.resolveRecord({
recordType: TYPE
recordId: ID,
isEditMode: true
})
sublist.setSublistValue({
line: LINE,
id: 'YOUR_ID',
value: `view / edit`
})
I am new to suitescript, I have been trying to generate a passwordGuid but it gives me a plain text and I expected something encrypted.
I have configured a digital ocean droplet as a SFTP client.
Generated the private and public key
I am using this code provided by one of the guys but I always don't get the expected password Guid
/**
*#NApiVersion 2.x
*#NScriptType Suitelet
*/
define([
'N/ui/serverWidget',
'N/https',
'N/log',
'N/url'
],
function (
ui,
https,
log,
url
) {
var HOST_KEY_TOOL_URL = 'https://ursuscode.com/tools/sshkeyscan.php?url=';
function getFormTemplate() {
var form;
form = ui.createForm({
title: 'Password Form'
});
form.addSubmitButton({
label: 'Submit'
});
return form;
}
function addSelectorFields(form) {
var select = form.addField({
id: 'selectaction',
type: ui.FieldType.SELECT,
label: 'Select Action'
});
select.addSelectOption({
value: 'getpasswordguid',
text: 'Get Password GUID'
});
select.addSelectOption({
value: 'gethostkey',
text: 'Get Host Key'
});
return form;
}
function addPasswordGUID1Fields(form) {
var frm = form;
frm.addField({
id: 'restricttoscriptids',
type: ui.FieldType.TEXT,
label: 'Restrict To Script Ids'
}).isMandatory = true;
frm.addField({
id: 'restricttodomains',
type: ui.FieldType.TEXT,
label: 'Restrict To Domains'
}).isMandatory = true;
return frm;
}
function addPasswordGUID2Fields(form, restrictToScriptIds, restrictToDomains) {
form.addCredentialField({
id: 'password',
label: 'Password',
restrictToScriptIds: restrictToScriptIds.replace(' ', '').split(','),
restrictToDomains: restrictToDomains.replace(' ', '').split(',')
});
return form;
}
function addHostKeyFields(form) {
form.addField({
id: 'url',
type: ui.FieldType.TEXT,
label: 'URL (Required)'
});
form.addField({
id: 'port',
type: ui.FieldType.INTEGER,
label: 'Port (Optional)'
});
form.addField({
id: 'hostkeytype',
type: ui.FieldType.TEXT,
label: 'Type (Optional)'
});
return form;
}
function onRequest(option) {
var method;
var form;
var selectAction;
var port;
var hostKeyType;
var restricttoscriptids;
var restricttodomains;
var password;
var theResponse;
var myUrl;
var url;
method = option.request.method;
form = getFormTemplate(method);
if (method === 'GET') {
form = addSelectorFields(form);
}
if (method === 'POST') {
selectAction = option.request.parameters.selectaction;
if (selectAction === 'getpasswordguid') {
form = addPasswordGUID1Fields(form);
} else if (selectAction === 'gethostkey') {
form = addHostKeyFields(form);
} else {
password = option.request.parameters.password;
log.debug("password",password)
log.debug("URL",url)
log.debug("port",port)
log.debug("hostKeyType",hostKeyType)
log.debug("restricttoscriptids",restricttoscriptids)
log.debug("restricttodomains",restricttodomains)
url = option.request.parameters.url;
port = option.request.parameters.port;
hostKeyType = option.request.parameters.hostkeytype;
restricttoscriptids = option.request.parameters.restricttoscriptids;
restricttodomains = option.request.parameters.restricttodomains;
if (restricttoscriptids && restricttodomains) {
form = addPasswordGUID2Fields(form, restricttoscriptids, restricttodomains);
}
if (password) {
form.addField({
id: 'passwordguidresponse',
type: ui.FieldType.LONGTEXT,
label: 'PasswordGUID Response',
displayType: ui.FieldDisplayType.INLINE
}).defaultValue = password;
}
if (url) {
myUrl = HOST_KEY_TOOL_URL + url + '&port=' + port + '&type=' + hostKeyType;
theResponse = https.get({ url: myUrl }).body;
form.addField({
id: 'hostkeyresponse',
type: ui.FieldType.LONGTEXT,
label: 'Host Key Response',
displayType: ui.FieldDisplayType.INLINE
}).defaultValue = theResponse;
}
}
}
option.response.writePage(form);
}
return {
onRequest: onRequest
};
});
I'm wondering if it is possible to use if statements this way so that the command gives separate messages depending on the users role. This is one attempt I've made but the second if statement is unreachable.
module.exports = {
name: "train",
description: "Train to earn some reputation!",
async execute(client, message, args, cmd, discord, profileData) {
const events1 = [
"sample event",
"sample event",
"sample event",
];
const events2 = [
"sample event",
"sample event",
"sample event",
];
const injuries = [
"sample injury",
"sample injury",
"sample injury",
];
const chosenEvent1 = events1.sort(() => Math.random() - Math.random()).slice(0, 1);
const chosenEvent2 = events2.sort(() => Math.random() - Math.random()).slice(0, 1);
const chosenInjury = injuries.sort(() => Math.random() - Math.random()).slice(0, 1);
const randomNumber1 = Math.floor(Math.random() * 29) + 5;
const randomNumber2 = Math.floor(Math.random() * 9) + 1;
if((!message.member.roles.cache.has('roleid#1'))); {
if(Math.floor(Math.random() * 3) === 0) {
await profileModel.findOneAndUpdate(
{
userID: message.author.id,
},
{
$inc: {
health: -randomNumber2,
},
}
);
return message.channel.send(`${chosenInjury} You lost ${randomNumber2} health and gained no reputation.`);
} else {
await profileModel.findOneAndUpdate(
{
userID: message.author.id,
},
{
$inc: {
reputation: randomNumber1,
},
}
);
return message.channel.send(`${chosenEvent1} You earned ${randomNumber1} reputation!`);
}
if((!message.member.roles.cache.has('roleid#2'))); {
return message.channel.send(`${chosenEvent2} You earned ${randomNumber1} reputation!`);
}
}}
};
So ideally if you have RoleID #1, you have a chance of injury or your reputation increases and you get a message with Event1 prompts. If you have RoleID #2, your reputation just increases and you get a message with Event2 prompts. I hope this is clear.
It seems the brackets are bit off and that is why the code is unreachable.
I have also attached an image of the locations where I made code changes. A key consideration is in javascript try to avoid placing semicolons right after an if statement
Please see the small bracket changes I made in your code example:
module.exports = {
name: 'train',
description: 'Train to earn some reputation!',
async execute(client, message, args, cmd, discord, profileData) {
const events1 = ['sample event', 'sample event', 'sample event'];
const events2 = ['sample event', 'sample event', 'sample event'];
const injuries = ['sample injury', 'sample injury', 'sample injury'];
const chosenEvent1 = events1.sort(() => Math.random() - Math.random()).slice(0, 1);
const chosenEvent2 = events2.sort(() => Math.random() - Math.random()).slice(0, 1);
const chosenInjury = injuries.sort(() => Math.random() - Math.random()).slice(0, 1);
const randomNumber1 = Math.floor(Math.random() * 29) + 5;
const randomNumber2 = Math.floor(Math.random() * 9) + 1;
if (!message.member.roles.cache.has('roleid#1')) {
if (Math.floor(Math.random() * 3) === 0) {
await profileModel.findOneAndUpdate(
{
userID: message.author.id,
},
{
$inc: {
health: -randomNumber2,
},
}
);
return message.channel.send(
`${chosenInjury} You lost ${randomNumber2} health and gained no reputation.`
);
} else {
await profileModel.findOneAndUpdate(
{
userID: message.author.id,
},
{
$inc: {
reputation: randomNumber1,
},
}
);
return message.channel.send(`${chosenEvent1} You earned ${randomNumber1} reputation!`);
}
} else if (!message.member.roles.cache.has('roleid#2')) {
return message.channel.send(`${chosenEvent2} You earned ${randomNumber1} reputation!`);
}
},
};
First, let me say, I am very new to backend application and Nodejs. I primarily do mobile development, so my knowledge of the language is limited.
I have an endpoint in Firebase Functions that builds and saves a PDF from data in Firestore and images in Storage. The PDF building works just fine, and I am not getting any errors. However, the final piece of code to save the PDF doesn't execute consistently. I have log statements that never get fired, but sometimes the PDF is saved. I assume it has something to do with my use of async methods but I'm not sure. Is there anything blatantly wrong with this code? This is the entirety of the code I am using.
const admin = require('firebase-admin');
const firebase_tools = require('firebase-tools');
const functions = require('firebase-functions');
const Printer = require('pdfmake');
const fonts = require('pdfmake/build/vfs_fonts.js');
const {Storage} = require('#google-cloud/storage');
const url = require('url');
const https = require('https')
const os = require('os');
const fs = require('fs');
const path = require('path');
const storage = new Storage();
const bucketName = '<BUCKET NAME REMOVED FOR THIS QUESTION>'
admin.initializeApp({
serviceAccountId: 'firebase-adminsdk-ofnne#perimeter1-d551f.iam.gserviceaccount.com',
storageBucket: bucketName
});
const bucket = admin.storage().bucket()
const firestore = admin.firestore()
const fontDescriptors = {
Roboto: {
normal: Buffer.from(fonts.pdfMake.vfs['Roboto-Regular.ttf'], 'base64'),
bold: Buffer.from(fonts.pdfMake.vfs['Roboto-Medium.ttf'], 'base64'),
italics: Buffer.from(fonts.pdfMake.vfs['Roboto-Italic.ttf'], 'base64'),
bolditalics: Buffer.from(fonts.pdfMake.vfs['Roboto-Italic.ttf'], 'base64'),
}
};
function buildLog(data) {
const filePath = data.imageReference;
const fileName = path.basename(filePath);
const tempFilePath = path.join(os.tmpdir(), fileName);
return {
stack: [
{
image: tempFilePath,
fit: [130, 220]
},
{
text: data["logEventType"],
style: 'small'
},
{
text: data["date"],
style: 'small'
}
],
unbreakable: true,
width: 130
}
}
function buildLogsBody(data) {
var body = [];
var row = []
var count = 0
data.forEach(function(logData) {
const log = buildLog(logData)
row.push(log)
count = count + 1
if (count == 4) {
body.push([{columns: row, columnGap: 14}])
body.push([{text: '\n'}])
row = []
count = 0
}
});
body.push([{columns: row, columnGap: 14}])
return body;
}
function title(incidentTitle, pageNumber, logCount, messageCount) {
var pageTitle = "Incident Summary"
const logPageCount = Math.ceil(logCount / 8)
if (messageCount > 0 && pageNumber > logPageCount) {
pageTitle = "Message History"
}
var body = [{
text: incidentTitle + ' | ' + pageTitle,
style: 'header'
}]
return body
}
function messageBody(message) {
var body = {
stack: [
{
columns: [
{width: 'auto', text: message['senderName'], style: 'messageSender'},
{text: message['date'], style: 'messageDate'},
],
columnGap: 8,
lineHeight: 1.5
},
{text: message['content'], style: 'message'},
{text: '\n'}
],
unbreakable: true
}
return body
}
function buildMessageHistory(messages) {
var body = []
if (messages.length > 0) {
body.push({ text: "", pageBreak: 'after' })
}
messages.forEach(function(message) {
body.push(messageBody(message))
body.push('\n')
})
return body
}
const linebreak = "\n"
async function downloadImages(logs) {
await Promise.all(logs.map(async (log) => {
functions.logger.log('Image download started for ', log);
const filePath = log.imageReference;
const fileName = path.basename(filePath);
const tempFilePath = path.join(os.tmpdir(), fileName);
await bucket.file(filePath).download({destination: tempFilePath});
functions.logger.log('Image downloaded locally to', tempFilePath);
}));
}
//////////// PDF GENERATION /////////////////
exports.generatePdf = functions.https.onCall(async (data, context) => {
console.log("PDF GENERATION STARTED **************************")
// if (request.method !== "GET") {
// response.send(405, 'HTTP Method ' + request.method + ' not allowed');
// return null;
// }
const teamId = data.teamId;
const incidentId = data.incidentId;
const incidentRef = firestore.collection('teams/').doc(teamId).collection('/history/').doc(incidentId);
const incidentDoc = await incidentRef.get()
const messages = []
const logs = []
if (!incidentDoc.exists) {
throw new functions.https.HttpsError('not-found', 'Incident history not found.');
}
const incident = incidentDoc.data()
const incidentTitle = incident["name"]
const date = "date" //incident["completedDate"]
const address = incident["address"]
const eventLogRef = incidentRef.collection('eventLog')
const logCollection = await eventLogRef.get()
logCollection.forEach(doc => {
logs.push(doc.data())
})
functions.logger.log("Checking if images need to be downloaded");
if (logs.length > 0) {
functions.logger.log("Image download beginning");
await downloadImages(logs);
}
functions.logger.log("Done with image download");
const messagesRef = incidentRef.collection('messages')
const messageCollection = await messagesRef.get()
messageCollection.forEach(doc => {
messages.push(doc.data())
})
////////////// DOC DEFINITION ///////////////////////
const docDefinition = {
pageSize: { width: 612, height: 792 },
pageOrientation: 'portrait',
pageMargins: [24,60,24,24],
header: function(currentPage, pageCount, pageSize) {
var headerBody = {
columns: [
title(incidentTitle, currentPage, logs.length, messages.length),
{
text: 'Page ' + currentPage.toString() + ' of ' + pageCount,
alignment: 'right',
style: 'header'
}
],
margin: [24, 24, 24, 0]
}
return headerBody
},
content: [
date,
linebreak,
address,
linebreak,
{ text: [
{ text: 'Incident Commander:', style: 'header' },
{ text: ' Daniel', style: 'regular'},
]
},
linebreak,
{
text: [
{ text: 'Members involved:', style: 'header' },
{text: ' Shawn, Zack, Gabe', style: 'regular'},
]
},
linebreak,
buildLogsBody(logs),
buildMessageHistory(messages)
],
pageBreakBefore: function(currentNode, followingNodesOnPage, nodesOnNextPage, previousNodesOnPage) {
return currentNode.headlineLevel === 1 && followingNodesOnPage.length === 0;
},
styles: {
header: {
fontSize: 16,
bold: true
},
regular: {
fontSize: 16,
bold: false
},
messageSender: {
fontSize: 14,
bold: true
},
message: {
fontSize: 14
},
messageDate: {
fontSize: 14,
color: 'gray'
}
}
}
const printer = new Printer(fontDescriptors);
const pdfDoc = printer.createPdfKitDocument(docDefinition);
var chunks = []
const pdfName = `${teamId}/${incidentId}/report.pdf`;
pdfDoc.on('data', function (chunk) {
chunks.push(chunk);
});
pdfDoc.on('end', function () {
functions.logger.log("PDF on end started")
const result = Buffer.concat(chunks);
// Upload generated file to the Cloud Storage
const fileRef = bucket.file(
pdfName,
{
metadata: {
contentType: 'application/pdf'
}
}
);
// bucket.upload("report.pdf", { destination: "${teamId}/${incidentId}/report.pdf", public: true})
fileRef.save(result);
fileRef.makePublic().catch(console.error);
// Sending generated file as a response
// res.send(result);
functions.logger.log("File genderated and saved.")
return { "response": result }
});
pdfDoc.on('error', function (err) {
res.status(501).send(err);
throw new functions.https.HttpsError('internal', err);
});
pdfDoc.end();
})
For quick reference, the main endpoint method is the exports.generatePdf and the pdfDoc.on at the end is the code that should handle the saving, but code appears to never fire, as the logs in it are never logged, and the document is not being saved always.
This is a function lifecycle issue, your function is killed prior to completing its task because the last operation you do deal with an event handler instead of returning a Promise. The reason it sometimes works is only because you got lucky. Once a function is complete, it should have finished doing everything it needs to.
So what you need to do is correctly pipe the data from the pdfDoc stream through to Cloud Storage, all wrapped in Promise that Cloud Functions can use to monitor progress and not kill your function before it finishes.
In it's simplest form it looks like this:
const stream = /* ... */;
const storageStream = bucket
.file(/* path */)
.createWriteStream(/* options */);
return new Promise((resolve, reject) => {
storageStream.once("finish", resolve); // resolve when written
storageStream.once("error", reject); // reject when either stream errors
stream.once("error", reject);
stream.pipe(storageStream); // pipe the data
});
Note: The Google Cloud Storage Node SDK is not the same as the Firebase Client's Cloud Storage SDK!
return new Promise((resolve, reject) => {
const pdfDoc = printer.createPdfKitDocument(docDefinition);
const pdfName = `${teamId}/${incidentId}/report.pdf`;
// Reference to Cloud Storage upload location
const fileRef = bucket.file(pdfName);
const pdfReadStream = pdfDoc;
const storageWriteStream = fileRef.createWriteStream({
predefinedAcl: 'publicRead', // saves calling makePublic()
contentType: 'application/pdf'
});
// connect errors from the PDF
pdfReadStream.on('error', (err) => {
console.error("PDF stream error: ", err);
reject(new functions.https.HttpsError('internal', err));
});
// connect errors from Cloud Storage
storageWriteStream.on('error', (err) => {
console.error("Storage stream error: ", err);
reject(new functions.https.HttpsError('internal', err));
});
// connect upload is complete event.
storageWriteStream.on('finish', () => {
functions.logger.log("File generated and saved to Cloud Storage.");
resolve({ "uploaded": true });
});
// pipe data through to Cloud Storage
pdfReadStream.pipe(storageWriteStream);
// finish the document
pdfDoc.end();
});
I'm trying to export excel file using exceljs library. I'm using AngularJS and NodeJS.
Here is my code:
HTML:
<a class="btn m-b-xs btn-info btn-doc" ng-click="exportExcel()" style='background-color: #34495e; margin-left: 5%;'>
</a>
Controller:
$scope.exportExcel = function() {
$http.post('/api/exportExcel/exportExcel', {"data": $scope.data});
};
NodeJS:
const Excel = require('exceljs');
export async function exportExcel(req, res) {
try {
var workbook = new Excel.Workbook();
var worksheet = workbook.addWorksheet('My Sheet');
worksheet.columns = [
{ header: 'Id', key: 'id', width: 10 },
{ header: 'Name', key: 'name', width: 32 },
{ header: 'D.O.B.', key: 'DOB', width: 10 }
];
worksheet.addRow({id: 1, name: 'John Doe', dob: new Date(1970,1,1)});
worksheet.addRow({id: 2, name: 'Jane Doe', dob: new Date(1965,1,7)});
var tempFilePath = tempfile('.xlsx');
workbook.xlsx.writeFile(tempFilePath).then(function() {
console.log('file is written');
res.sendFile(tempFilePath, function(err){
console.log('---------- error downloading file: ' + err);
});
});
} catch(err) {
console.log('OOOOOOO this is the error: ' + err);
}
}
I've just found this example of code for generating excel just to try get excel file on client side and after that i will create my own file.
But for now i just get in log this error
file is written
(node:25624) UnhandledPromiseRejectionWarning: TypeError: res.sendFile is not a function
Does anyone can help me to get excel file in browser after i click on button for export?
UPDATE
controller:
$scope.exportExcel = function() {
$http.post('/api/exportExcel/exportExcel', {"offer": $scope.offer})
.then(function(response) {
console.log(response.data);
var data = response.data,
blob = new Blob([data], { type: response.headers('content-type') }),
url = $window.URL || $window.webkitURL;
$scope.fileUrl = url.createObjectURL(blob);
});
};
html:
<a class="btn m-b-xs btn-info btn-doc" ng-click="exportExcel()" ng-href="{{ fileUrl }}" download="table.xlsx">
<i class="fa"></i>Export</a>
There were some problems, I have corrected them check and verify.
Definition of Route:-
var express = require("express");
var router = express.Router();
var fs = require("fs");
const Excel = require("exceljs");
var path = require("path");
router.get("/", async function(req, res, next) {
console.log("---InSideFunction---");
try {
var workbook = new Excel.Workbook();
var worksheet = workbook.addWorksheet();
worksheet.columns = [
{ header: "Id", key: "id", width: 10 },
{ header: "Name", key: "name", width: 32 },
{ header: "D.O.B.", key: "DOB", width: 10 }
];
worksheet.addRow({ id: 1, name: "John Doe", DOB: new Date(1970, 1, 1) });
worksheet.addRow({ id: 2, name: "Jane Doe", DOB: new Date(1965, 1, 7) });
workbook.xlsx
.writeFile("newSaveeee.xlsx")
.then(response => {
console.log("file is written");
console.log(path.join(__dirname, "../newSaveeee.xlsx"));
res.sendFile(path.join(__dirname, "../newSaveeee.xlsx"));
})
.catch(err => {
console.log(err);
});
} catch (err) {
console.log("OOOOOOO this is the error: " + err);
}
});
module.exports = router;
req and res are not associated with 'exceljs'.