How to display shop timing in a week nodejs - node.js

I have a key array of objects which user have for days and their timings for their shop i.e
user A -
{
"name" : "shop1",
"timings" : [
{
"Monday" : "10am to 7pm"
},
{
"Thursday" : "Closed"
},
{
"Friday" : "9am to 6pm"
},
{
"Sunday" : "Closed"
},
{
"Wednesday" : "10am to 7pm"
},
{
"Tuesday" : "10am to 7pm"
},
{
"Saturday" : "10am to 7pm"
}
]}
Now I need to show these timing by days in frontend i.e
Monday-Saturday:9am to 7pm, Thursday,Sunday: Closed
So far I've tried to loop them and searched all days which are closed i.e
function filterByValue(array, string) {
return array.filter(o =>
Object.keys(o).some(k => o[k].toLowerCase().includes(string.toLowerCase())));
}
let closedDays = filterByValue(element.timings, 'Closed')
console.log("whtt",closedDays); // [{name: 'Lea', country: 'Italy'}]
let res = closedDays.map(x => Object.keys(x)[0]);
but for open days I can't figure out any suitable solution which can be used here. It would be a great help if you suggest something

const timings = [{"Monday": "10am to 7pm"}, {"Thursday": "Closed"}, {"Friday": "9am to 6pm"}, {"Sunday": "Closed"}, {"Wednesday": "10am to 7pm"}, {"Tuesday": "10am to 7pm"}, {"Saturday": "10am to 7pm"}];
const weekDaysInOrder = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
//Step1 little change list of objects
const newTimings = changeTimingsStructure(timings);
let obj = {}
sortTimings(newTimings).forEach(el => {
if (el.timing in obj) {
const maxIndex = Math.max(...obj[el.timing].map(el => el.indexOfDay));
if (maxIndex + 1 < el.indexOfDay && el.timing !== 'Closed') {
obj = pushToObjectArr(el.timing + '-v2', el, obj);
} else {
obj = pushToObjectArr(el.timing, el, obj);
}
} else {
obj = pushToObjectArr(el.timing, el, obj);
}
})
displayTimings(obj);
function changeTimingsStructure(timings) {
const newTimings = [];
timings.forEach(day => {
const dayName = Object.keys(day)[0]
const timing = day[dayName];
newTimings.push({
day: dayName,
timing,
indexOfDay: weekDaysInOrder.findIndex(dayInWeek => dayInWeek === dayName)
})
})
return newTimings;
}
function sortTimings(timings) {
return timings.sort(((a, b) => a.indexOfDay - b.indexOfDay));
}
function pushToObjectArr(key, el, obj) {
if (key in obj) {
obj[key].push(el);
} else {
obj[key] = [el]
}
return obj
}
function displayTimings(obj) {
let closedText = '';
for (const groupOfDays in obj) {
if (groupOfDays !== 'Closed') {
if (obj[groupOfDays].length > 1) {
console.log(`${obj[groupOfDays][0].day} - ${obj[groupOfDays][obj[groupOfDays].length - 1].day}: ${groupOfDays}`);
} else {
console.log(`${obj[groupOfDays][0].day}: ${obj[groupOfDays][0].timing}`);
}
} else {
closedText = `${obj[groupOfDays].map(dayObj => dayObj.day).join(', ')}: Closed`;
}
}
console.log(closedText);
}
Try this, final output:

Here I have created this utility in order to format the input as per your expected output.
let data = {name:'shop1',timings:[{Monday:'10am to 7pm'},{Thursday:'Closed'},{Friday:'9am to 6pm'},{Sunday:'Closed'},{Wednesday:'10am to 7pm'},{Tuesday:'10am to 7pm'},{Saturday:'10am to 7pm'}]};
const formatData = data => {
return data.reduce(
(result, d) => {
const value = Object.values(d)[0];
const key = Object.keys(d)[0];
if (value === 'Closed') {
result.closed.days.push(key);
if (!result.closed.values) {
result.closed.value = value;
}
} else {
result.open.days.push(key);
result.open.timings.push(value);
}
return result;
},
{
closed: { days: [], value: '' },
open: { days: [], timings: [] },
}
);
};
const formattedData = formatData(data.timings);
const formatTimings = timings => {
const formattedTimings= timings.reduce((result, time) => {
const times = time
.split('to')
.map(t => parseInt(t.replace(/\D+/g, '')))
.filter(Boolean);
result.from.push(times[0]);
result.to.push(times[1]);
return result
}, {from :[], to: []});
return `${Math.min(...formattedTimings.from)}am to ${Math.max(...formattedTimings.to)}pm`
};
const openDaysTimings = formatTimings(formattedData.open.timings);
const displayTimings = (days, time) => {
return `${days.join(", ")}: ${time}`
}
console.log(displayTimings(formattedData.closed.days, formattedData.closed.value));
console.log(displayTimings(formattedData.open.days, openDaysTimings));

Related

Generate 1000 pdf with survey pdf

I'm trying to generate more than one thousand pdf files using surveyPDF.
The problem is that i can generate only 80 pdf files...
I'm passing an array with more than 1000 pdf to generate.
Code :
query.map(async items => {
const { generatePdf } = await import("~/lib/survey");
const filename = kebabCase(
`${campaign.title} - ${items.employee.fullName.toLowerCase()} -${moment().format("DD/MM/YYYY - HH:mm")} `
);
return generatePdf(campaign.template.body, items, filename, 210, 297);
});
The code which generate each pdfs :
import autoTable from "jspdf-autotable";
import { SurveyPDF, CommentBrick, CompositeBrick, PdfBrick, TextBrick } from "survey-pdf";
import { format } from "~/utils/date";
class AutoTableBrick extends PdfBrick {
constructor(question, controller, rect, options) {
super(question, controller, rect);
this.margins = {
top: controller.margins.top,
bottom: controller.margins.bot,
right: 30,
left: 30,
};
this.options = options;
}
renderInteractive() {
if (this.controller.doc.lastAutoTable && !this.options.isFirstQuestion) {
this.options.startY = this.yTop;
}
autoTable(this.controller.doc, {
head: [
[
{
content: this.question.title,
colSpan: 2,
styles: { fillColor: "#5b9bd5" },
},
],
],
margin: { ...this.margins },
styles: { fillColor: "#fff", lineWidth: 1, lineColor: "#5b9bd5", minCellWidth: 190 },
alternateRowStyles: { fillColor: "#bdd6ee" },
...this.options,
});
}
}
export async function generatePdf(json, data, filename, pdfWidth, pdfHeight) {
if (!json) {
return Promise.reject("Invalid json for pdf export");
}
for (const page of json.pages) {
page.readOnly = true;
}
const surveyPDF = new SurveyPDF(json, {
fontSize: 11,
format: [pdfWidth, pdfHeight],
commercial: true,
textFieldRenderAs: "multiLine",
});
surveyPDF.showQuestionNumbers = "off";
surveyPDF.storeOthersAsComment = false;
//TODO This does not work well with dynamic dropdown, bug declared
// surveyPDF.mode = "display";
surveyPDF.mergeData({ ...data, _: {} });
surveyPDF.onRenderQuestion.add(function(survey, options) {
const { bricks, question } = options;
if (question.getType() === "comment" && question.value && bricks.length > 0) {
for (const brick of bricks) {
if (brick.value) {
brick.value = question.value.replace(/\t/g, " ");
}
if (brick instanceof CompositeBrick) {
const { bricks } = brick;
for (const brick of bricks) {
if (brick instanceof CommentBrick) {
brick.value = question.value.replace(/\t/g, " ");
}
}
}
}
}
});
surveyPDF.onRenderQuestion.add(async function(survey, options) {
const {
point,
bricks,
question,
controller,
module: { SurveyHelper },
} = options;
if (question.getType() === "multipletext") {
const body = [];
let extraRows = 0;
let rows = question.getRows();
for (let i = 0; i < rows.length; i++) {
for (let j = 0; j < rows[i].length; j++) {
let { title, value, inputType } = rows[i][j];
if (inputType === "date") {
value = format(value);
}
if (typeof value === "string" && value.length > 0) {
const valueEstRows = value.match(/.{1,70}/g).length;
if (valueEstRows > 1) {
extraRows += valueEstRows;
}
}
body.push([title, value || "N/A"]);
}
}
//TODO Use SurveyHelper helperDoc do calculate the height of the auto table
const startY = point.yTop;
const height = 21.5 * (body.length + 1) + 8.5 * extraRows;
const isFirstQuestion = question.title === question.parent.questions[0].title;
options.bricks = [
new AutoTableBrick(question, controller, SurveyHelper.createRect(point, bricks[0].width, height), {
startY,
body,
isFirstQuestion,
}),
];
}
});
surveyPDF.onRenderQuestion.add(async function(survey, options) {
const {
point,
question,
controller,
module: { SurveyHelper },
} = options;
if (question.getType() === "text") {
//Draw question background
const { default: backImage } = await import("~/public/assets/images/block.png");
const backWidth = SurveyHelper.getPageAvailableWidth(controller);
const backHeight = SurveyHelper.pxToPt(100);
const imageBackBrick = SurveyHelper.createImageFlat(point, null, controller, backImage, backWidth, backHeight);
options.bricks = [imageBackBrick];
point.xLeft += controller.unitWidth;
point.yTop += controller.unitHeight;
const oldFontSize = controller.fontSize;
const titleBrick = await SurveyHelper.createTitleFlat(point, question, controller);
controller.fontSize = oldFontSize;
titleBrick.unfold()[0]["textColor"] = "#6a6772";
options.bricks.push(titleBrick);
//Draw text question text field border
let { default: textFieldImage } = await import("~/public/assets/images/input.png");
let textFieldPoint = SurveyHelper.createPoint(imageBackBrick);
textFieldPoint.xLeft += controller.unitWidth;
textFieldPoint.yTop -= controller.unitHeight * 3.3;
let textFieldWidth = imageBackBrick.width - controller.unitWidth * 2;
let textFieldHeight = controller.unitHeight * 2;
let imageTextFieldBrick = SurveyHelper.createImageFlat(
textFieldPoint,
null,
controller,
textFieldImage,
textFieldWidth,
textFieldHeight
);
options.bricks.push(imageTextFieldBrick);
textFieldPoint.xLeft += controller.unitWidth / 2;
textFieldPoint.yTop += controller.unitHeight / 2;
let textFieldValue = question.value || "";
if (textFieldValue.length > 90) {
textFieldValue = textFieldValue.substring(0, 95) + "...";
}
const textFieldBrick = await SurveyHelper.createBoldTextFlat(
textFieldPoint,
question,
controller,
textFieldValue
);
controller.fontSize = oldFontSize;
textFieldBrick.unfold()[0]["textColor"] = "#EFF8FF";
options.bricks.push(textFieldBrick);
}
});
surveyPDF.onRenderQuestion.add(async function(survey, options) {
const {
point,
question,
controller,
module: { SurveyHelper },
} = options;
if (question.getType() === "radiogroup" || question.getType() === "rating") {
options.bricks = [];
const oldFontSize = controller.fontSize;
const titleLocation = question.hasTitle ? question.getTitleLocation() : "hidden";
let fieldPoint;
if (["hidden", "matrix"].includes(titleLocation)) {
fieldPoint = SurveyHelper.clone(point);
} else {
const titleBrick = await SurveyHelper.createTitleFlat(point, question, controller);
titleBrick.xLeft += controller.unitWidth;
titleBrick.yTop += controller.unitHeight * 2;
controller.fontSize = oldFontSize;
titleBrick.unfold()[0]["textColor"] = "#6a6772";
options.bricks.push(titleBrick);
fieldPoint = SurveyHelper.createPoint(titleBrick);
fieldPoint.yTop += controller.unitHeight * 1.3;
}
//Draw checkbox question items field
const { default: itemEmptyImage } = await import("~/public/assets/images/unchecked.png");
const { default: itemFilledImage } = await import("~/public/assets/images/checked.png");
const itemSide = controller.unitWidth;
let imageItemBrick;
const choices = question.getType() === "rating" ? question.visibleRateValues : question.visibleChoices;
for (const choice of choices) {
const isItemSelected =
question.getType() === "rating" ? question.value === choice.value : choice === question.selectedItem;
imageItemBrick = SurveyHelper.createImageFlat(
fieldPoint,
null,
controller,
isItemSelected ? itemFilledImage : itemEmptyImage,
itemSide,
itemSide
);
options.bricks.push(imageItemBrick);
const textPoint = SurveyHelper.clone(fieldPoint);
textPoint.xLeft += itemSide + controller.unitWidth / 2;
textPoint.yTop += itemSide / 12;
const itemValue = choice.locText.renderedHtml;
const checkboxTextBrick = await SurveyHelper.createTextFlat(
textPoint,
question,
controller,
itemValue,
TextBrick
);
checkboxTextBrick.unfold()[0]["textColor"] = "#6a6772";
fieldPoint.yTop = imageItemBrick.yBot + SurveyHelper.GAP_BETWEEN_ROWS * controller.unitHeight;
options.bricks.push(checkboxTextBrick);
}
}
});
surveyPDF.onRenderFooter.add(function(survey, canvas) {
canvas.drawText({
text: canvas.pageNumber + "/" + canvas.countPages,
fontSize: 10,
horizontalAlign: "right",
margins: {
right: 12,
},
});
});
return await surveyPDF.raw(`./pdf/${filename}.pdf`);
}
The error :
FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
I have already try to increase the node memory using $env:NODE_OPTIONS="--max-old-space-size=8192"

How to dynamically change the version from package.json?

I get version 1.0.1 (server_version) from the server
I compare what is in process.env.version === server_version
If this is the case, then I change process.env.version = server_version.
However, I just can't do it from the client side.
All this is needed to request an update from the user, that is, when a new version is released, then ask, and then do $router.go()
If you're using Vue-CLI - you can do the same as me:
// vue.config.js
module.exports =
{
chainWebpack: config =>
{
config.plugin('define')
.tap(args =>
{
args[0]['process.env'].BUILD_TIME = webpack.DefinePlugin.runtimeValue(Date.now, ['./package.json']);
args[0]['process.env'].VERSION = JSON.stringify(pk.version);
return args;
});
return config;
}
}
Then, in your public/index.html
<!DOCTYPE html>
<html>
<head>
.....
<template id="VERSION"><%= process.env.VERSION %></template>
.....
</html>
Then, in your src/main.js
import mixinVersion from './mixins/mixinVersion'
import { version } from '../package.json'
.....
new Vue({
mixins: [mixinVersion],
computed:
{
appVersion()
{
const buildTime = new Date(process.env.BUILD_TIME);
return version + ' (' + buildTime.toLocaleString('en', {
year: 'numeric',
day: 'numeric',
month: 'short',
hour: 'numeric',
minute: 'numeric',
}) + ')';
},
},
created()
{
this.firstVersionCheck();
}
});
Then in src/mixins/mixinVersion.js
import { version } from '#/../package.json';
const checkingPeriod = 200; // in seconds
function isNewerVersion(_old, _new)
{
// return true if SemVersion A is newer than B
const oldVer = _old.split('.');
const newVer = _new.split('.');
if (+oldVer[0] < +newVer[0]) return false;
if (+oldVer[0] > +newVer[0]) return true;
if (+oldVer[1] < +newVer[1]) return false;
if (+oldVer[1] > +newVer[1]) return true;
return +oldVer[2] > +newVer[2];
}
export default
{
data()
{
return {
newVersionExists: false,
timerVersion: null,
lastVersionCheck: null,
windowHiddenProp: '',
};
},
watch:
{
newVersionExists(newVal, oldVal)
{
// if the user decides to dismiss and not refresh - we must continue checking
if (oldVal && !newVal) this.scheduleVersion();
},
},
methods:
{
firstVersionCheck()
{
this.lastVersionCheck = Date.now();
// Set the name of the hidden property and the change event for visibility
let visibilityChange;
if (typeof document.hidden !== 'undefined')
{
// Opera 12.10 and Firefox 18 and later support
this.windowHiddenProp = 'hidden';
visibilityChange = 'visibilitychange';
}
else if (typeof document.msHidden !== 'undefined')
{
this.windowHiddenProp = 'msHidden';
visibilityChange = 'msvisibilitychange';
}
else if (typeof document.webkitHidden !== 'undefined')
{
this.windowHiddenProp = 'webkitHidden';
visibilityChange = 'webkitvisibilitychange';
}
document.addEventListener(visibilityChange, this.handlePageVisibility, false);
this.scheduleVersion();
},
handlePageVisibility()
{
if (!document[this.windowHiddenProp])
{
// if too much time has passed in the background - immediately check for new version
if (Date.now() - this.lastVersionCheck > checkingPeriod * 1000)
{
if (this.timerVersion) clearTimeout(this.timerVersion);
this.checkVersion();
}
}
},
scheduleVersion()
{
// check for new versions
if (this.timerVersion) clearTimeout(this.timerVersion);
this.timerVersion = setTimeout(this.checkVersion, checkingPeriod * 1000); // check for new version every 3.3 minutes
},
checkVersion()
{
this.timerVersion = null;
fetch(process.env.BASE_URL + 'index.html', {
headers:
{
'X-SRS-Version': version,
}
}).then(response =>
{
if (response.status != 200) throw new Error('HTTP status = ' + response.status);
return response.text();
}).then(t =>
{
this.lastVersionCheck = Date.now();
const newVersion = t.match(/<template id="?VERSION"?>([^<]+)<\/template>/);
if (newVersion && newVersion[1])
{
if (isNewerVersion(newVersion[1], version))
{
if (!this.newVersionExists) // do not show multiple notifications
{
this.$snotify.confirm('There is a new version', 'New version', {
timeout: 0,
closeOnClick: false,
position: 'leftBottom',
buttons:
[
{
text: 'REFRESH',
action()
{
window.location.reload();
}
}
]
});
}
this.newVersionExists = true;
}
else this.scheduleVersion();
}
else this.scheduleVersion();
}).catch(err =>
{
if (navigator.onLine) console.error('Could not check for new version', err.message || err);
this.scheduleVersion();
});
},
}
};

How to dynamically generate schema for cube.js?

I have been working on a project to generate configurable dashboards.
so i have to generate schema dynamically based on an api request. is there any way to do that?
it will be very helpful if there is any working example!
There's an asyncModule function for this scenario. You can check example below:
const fetch = require('node-fetch');
const Funnels = require('Funnels');
asyncModule(async () => {
const funnels = await (await fetch('http://your-api-endpoint/funnels')).json();
class Funnel {
constructor({ title, steps }) {
this.title = title;
this.steps = steps;
}
get transformedSteps() {
return Object.keys(this.steps).map((key, index) => {
const value = this.steps[key];
let where = null
if (value[0] === PAGE_VIEW_EVENT) {
if (value.length === 1) {
where = `event = '${value[0]}'`
} else {
where = `event = '${value[0]}' AND page_title = '${value[1]}'`
}
} else {
where = `event = 'se' AND se_category = '${value[0]}' AND se_action = '${value[1]}'`
}
return {
name: key,
eventsView: {
sql: () => `select * from (${eventsSQl}) WHERE ${where}`
},
timeToConvert: index > 0 ? '30 day' : null
}
});
}
get config() {
return {
userId: {
sql: () => `user_id`
},
time: {
sql: () => `time`
},
steps: this.transformedSteps
}
}
}
funnels.forEach((funnel) => {
const funnelObject = new Funnel(funnel);
cube(funnelObject.title, {
extends: Funnels.eventFunnel(funnelObject.config),
preAggregations: {
main: {
type: `originalSql`,
}
}
});
});
})
More info: https://cube.dev/docs/schema-execution-environment#async-module

Paypal not returning custom variable

I'm trying to complete a paypal transaction using paypal-rest-sdk, everything is set up and working, however, I need to get the clientId back from paypal in the success route in order to save it in my client_feature_payment model. I found that we can set a "custom" field where we can set anything and that'll be sent back by paypal but this feautre is available in classic paypal sdk only and not available in rest-sdk one.
Is there any workaround for this?
//Paypal objects and methods from rest-sdk:
client_page: {
args: {
clientId: {
type: GraphQLString
}
},
type: ClientType,
resolve: async (_, args) => {
if (args.clientId) {
let clientMongoId = fromGlobalId(args.clientId).id;
let client = await Client.queryOne("id")
.eq(clientMongoId)
.exec();
let clientName = client.name;
let clientSecret = client.secret;
let company = await Company.queryOne("id")
.eq(client.companyId)
.exec();
let companyName = company.name;
let service = await Service.queryOne("id")
.eq(client.serviceId)
.exec();
let serviceName = service.name;
let clientFeature = await ClientFeature.query("clientId")
.eq(clientMongoId)
.exec();
let totalFeatures = [];
let clientFeatureId = [];
for (let i = 0; i < clientFeature.length; i++) {
clientFeatureId.unshift(clientFeature[i].id);
let feature = await Feature.query("id")
.eq(clientFeature[i].featureId)
.exec();
let newFeature;
feature.map(
feature =>
(newFeature = [
feature.name,
feature.cost,
feature.trial,
feature.frequency
])
);
totalFeatures.unshift(newFeature);
}
let trial, freq;
let cost = [];
totalFeatures.map(item => {
if (item[2] && item[3]) {
trial = item[2];
freq = item[3];
}
cost.unshift(item[1]);
});
const finalCost = cost.reduce((accumulator, currentValue) => {
return accumulator + currentValue;
}, 0);
let paypalFreq;
let frequencyInterval;
var isoDate = new Date(Date.now() + 1 * 60 * 1000);
switch (freq) {
case "bi-weekly":
paypalFreq = "DAY";
frequencyInterval = "7";
break;
case "monthly":
paypalFreq = "MONTH";
frequencyInterval = "1";
break;
case "3 months":
paypalFreq = "MONTH";
frequencyInterval = "3";
break;
case "6 months":
paypalFreq = "MONTH";
frequencyInterval = "6";
break;
case "1 year":
paypalFreq = "YEAR";
frequencyInterval = "1";
break;
default:
break;
}
var billingPlanAttributes = {
description:
"Create Plan for Trial & Frequency based payment for features and services used by customer",
merchant_preferences: {
auto_bill_amount: "yes",
cancel_url: "http://localhost:3000/cancel",
initial_fail_amount_action: "continue",
max_fail_attempts: "1",
return_url: "http://localhost:3000/success",
setup_fee: {
currency: "USD",
value: "0"
}
},
name: "Client Services & Features Charge",
payment_definitions: [
{
amount: {
currency: "USD",
value: finalCost
},
cycles: "0",
frequency: paypalFreq,
frequency_interval: frequencyInterval,
name: "Regular 1",
type: "REGULAR"
},
{
amount: {
currency: "USD",
value: "0"
},
cycles: "1",
frequency: "DAY",
frequency_interval: trial,
name: "Trial 1",
type: "TRIAL"
}
],
type: "INFINITE"
};
var billingPlanUpdateAttributes = [
{
op: "replace",
path: "/",
value: {
state: "ACTIVE"
}
}
];
var billingAgreementAttr = {
name: "Fast Speed Agreement",
description: "Agreement for Fast Speed Plan",
start_date: isoDate,
plan: {
id: "P-0NJ10521L3680291SOAQIVTQ"
},
payer: {
payment_method: "paypal",
payer_info: {
payer_id: clientMongoId
}
},
shipping_address: {
line1: "StayBr111idge Suites",
line2: "Cro12ok Street",
city: "San Jose",
state: "CA",
postal_code: "95112",
country_code: "US"
}
};
// Create the billing plan
let billingPlan = await new Promise((resolve, reject) => {
paypal.billingPlan.create(
billingPlanAttributes,
(error, billingPlan) => {
if (error) {
throw error;
} else {
resolve(billingPlan);
}
}
);
});
// let billingPlan = await billingPlanPromise;
// Activate the plan by changing status to Active
let billingAgreementAttributes = await new Promise(
(resolve, reject) => {
paypal.billingPlan.update(
billingPlan.id,
billingPlanUpdateAttributes,
(error, response) => {
if (error) {
throw error;
} else {
billingAgreementAttr.plan.id = billingPlan.id;
resolve(billingAgreementAttr);
}
}
);
}
);
// Use activated billing plan to create agreement
let approval_url = await new Promise((resolve, reject) => {
paypal.billingAgreement.create(
billingAgreementAttributes,
(error, billingAgreement) => {
if (error) {
throw error;
} else {
for (
var index = 0;
index < billingAgreement.links.length;
index++
) {
if (billingAgreement.links[index].rel === "approval_url") {
var approval_url = billingAgreement.links[index].href;
let newApprovalUrl =
approval_url + `&custom=${clientFeatureId}`;
resolve(newApprovalUrl);
// See billing_agreements/execute.js to see example for executing agreement
// after you have payment token
}
}
}
}
);
});
let data = {
companyId: companyName,
serviceId: serviceName,
name: clientName,
secret: clientSecret,
features: totalFeatures,
endpoint: approval_url
};
return Object.assign(data);
}
}
},
The success route:
app.get("/success", (req, res) => {
console.log("This is response", res);
let paymentToken = req.query.token;
paypal.billingAgreement.execute(paymentToken, {}, function(
error,
billingAgreement
) {
if (error) {
throw error;
} else {
console.log("Billing agreement", billingAgreement);
let date = billingAgreement.start_date;
let amountString =
billingAgreement.plan.payment_definitions[1].amount.value;
let trial =
billingAgreement.plan.payment_definitions[0].frequency_interval;
let frequencyInterval =
billingAgreement.plan.payment_definitions[1].frequency_interval;
let frequency = billingAgreement.plan.payment_definitions[1].frequency;
let totalFrequency = frequencyInterval + " " + frequency;
let period = [trial, totalFrequency];
let amount = parseInt(amountString);
try {
Payment.create({
id: uuidv1(),
date: date,
amount: amount,
period: period
});
} catch (err) {
throw new Error(err);
}
res.render("index");
}
});
});
My implementation looks very different, but I can output any payment details. My payment.execute() looks like so:
const express = require("express");
const paypal = require("paypal-rest-sdk");
const app = express;
paypal.configure({
mode: "sandbox", //sandbox or live
client_id:
"...",
client_secret:
"..."
});
app.set("view engine", "ejs");
app.get("/", (req, res) => res.render("index"));
app.post("/pay", (req, res) => {
const create_payment_json = {
intent: "sale",
payer: {
payment_method: "paypal"
},
redirect_urls: {
return_url: "http://localhost:3000/success",
cancel_url: "http://localhost:3000/cancel"
},
transactions: [
{
item_list: {
items: [
{
name: "Item",
sku: "001",
price: "3.33",
currency: "USD",
quantity: 1
}
]
},
amount: {
currency: "USD",
total: "3.33"
},
description:
"Hope this helps."
}
]
};
paypal.payment.create(create_payment_json, function(error, payment) {
if (error) {
throw error;
} else {
// console.log("Create Payment Response");
// console.log(payment.id);
// res.send('test')
for (let i = 0; i < payment.links.length; i++) {
if (payment.links[i].rel === "approval_url") {
res.redirect(payment.links[i].href);
}
}
}
});
});
app.get("/success", (req, res) => {
const payerId = req.query.PayerID;
const paymentId = req.query.paymentId;
const execute_payment_json = {
payer_id: payerId,
transactions: [
{
amount: {
currency: "USD",
total: "3.33"
}
}
]
};
paypal.payment.execute(paymentId, execute_payment_json, function(
error,
payment
) {
if (error) {
console.log(error.response);
throw error;
} else {
console.log(JSON.stringify(payment));
}
});
});
app.get("/cancel", (req, res) => {
res.send("Cancelled");
});
app.listen(3000, () => console.log("Server Started"));

Sequelize pagination

Using sequelize on my nodejs web app, I want to query posts using pagination (by date). Reading sequelize docs, they offer to use offset and limit.
Since I want to display the posts from new to old, I need to consider the date they were created. For example, if I limit the first query to 10 page, and before executing the second query a new post was created, the next query with offset of 10 will result a duplicate post from the last query.
How should I implement the pagination so it will support new entries?
The easiest way to do this is to use Sequelize's findAndCountAll
Post.findAndCountAll({
where: {...},
order: [...],
limit: 5,
offset: 0,
}).then(function (result) {
res.render(...);
});
Here, result has both the result of your query and count as result.rows and result.count. You can then increment the offset and use this for pagination.
Sequelize documentation for findAndCountAll
Try this:
const paginate = (query, { page, pageSize }) => {
const offset = page * pageSize;
const limit = pageSize;
return {
...query,
offset,
limit,
};
};
model.findAll(
paginate(
{
where: {}, // conditions
},
{ page, pageSize },
),
);
In order to avoid boilerplate code
If you want to have a stable pagination, don't paginate on row offset, since it's volatile, for the reason you mention.
You should aim for paginating on a value that is stable over time and use a where clause for filtering results. The best case would be if you have an auto-incrementing id, but the post date could also be reasonable.
Something like:
Post.findAll({
where: { createdDate: { $lt: previousDate },
limit: 10
})
You need to keep track of previousDate for this ofc. This approach also has some caveats, and you may need to combine it with client-side de-duplication.
Here is a blog post that probably has all the answers you need:
Pagination: You're (Probably) Doing It Wrong
With findAndCountAll here count is useful for pagination, from this total count we can limit as we want and also with async and await
let resAccidents = await ModalName.findAndCountAll({ where: { createdByID: employeeID }, offset: 0, limit: 10 });
this will return a count of total records as per where condition and 1st 10 records of it, then increase the value of offset to fetch further records.
You can simply do that
let limit = 10
let offset = 0 + (req.body.page - 1) * limit
Posts.findAndCountAll({
offset: offset,
limit: limit,
order: [
['date', 'ASC']
]
}).then(async result => {
return res.status(200).json({
status: true,
message: res.__('success'),
innerData: result
})
})
.catch(err => {
return validator.InvalidResponse(res, `${err}`)
})
Try this instead:
db.findAll({
offset: page_no,// your page number
limit:25,// your limit
This one solved my issue.
export const paginate = (query, schema) => {
let page = query.page ? query.page - 1 : 0;
page = page < 0 ? 0 : page;
let limit = parseInt(query.limit || 10);
limit = limit < 0 ? 10 : limit;
const offset = page * limit;
const where = {};
delete query.page;
delete query.limit;
Object.keys(schema).forEach((key) => {
schema[key] && query[key] ? (where[key] = query[key]) : null;
});
return {
where: where,
offset,
limit,
};
};
#Get()
findAll(#Query() query): unknown {
return this.model.findAll(paginate(query, {xx:1}));
}
/model?xx=yy&page=1&limit=5
var defered = Q.defer();
const offset = queryString.offset * queryString.limit;
const limit = queryString.limit;
var queryWhere = { class_id: { $ne: null }, section_id: { $ne: null } };
var searchClass = {};
var searchSection = {};
if (queryString) {
if (queryString.class && queryString.class !== "") {
searchClass = { class_id: { $eq: queryString.class } };
} else if (queryString.class && queryString.class === "") {
searchClass = { class_id: { $ne: null } };
}
if (queryString.section && queryString.section !== "") {
searchSection = { section_id: { $eq: queryString.section } };
} else if (queryString.section && queryString.section === "") {
searchSection = { section_id: { $ne: null } };
}
}
queryWhere = {
$and: [[searchClass], [searchSection]]
};
const schoolDB = require("../../db/models/tenant")(schema);
const Student = schoolDB.model("Student");
Student.findAll({
attributes: [
"id",
"first_name",
"last_name",
"profile_image_url",
"roll_number",
"emergency_contact_number"
],
offset: offset,
limit: limit,
where: queryWhere,
order: [["roll_number", "ASC"]]
})
.then(result => {
defered.resolve(result);
})
.catch(err => {
defered.reject(err);
});
Recommended using Sequelize's own operators
var defered = Q.defer();
const offset = queryString.offset * queryString.limit;
const limit = queryString.limit;
var queryWhere = { class_id: { $ne: null }, section_id: { $ne: null } };
var searchClass = {};
var searchSection = {};
if (queryString) {
if (queryString.class && queryString.class !== "") {
searchClass = { class_id: { $eq: queryString.class } };
} else if (queryString.class && queryString.class === "") {
searchClass = { class_id: { $ne: null } };
}
if (queryString.section && queryString.section !== "") {
searchSection = { section_id: { $eq: queryString.section } };
} else if (queryString.section && queryString.section === "") {
searchSection = { section_id: { $ne: null } };
}
}
queryWhere = {
$and: [[searchClass], [searchSection]]
};
const schoolDB = require("../../db/models/tenant")(schema);
const Student = schoolDB.model("Student");
Student.findAll({
attributes: [
"id",
"first_name",
"last_name",
"profile_image_url",
"roll_number",
"emergency_contact_number"
],
offset: offset,
limit: limit,
where: queryWhere,
order: [["roll_number", "ASC"]]
})
.then(result => {
defered.resolve(result);
})
.catch(err => {
defered.reject(err);
});

Resources