Unable to add user - couchdb

I'm trying to add user to CouchDB (3.1.0) installed on MacOS 10.15.5:
curl -u admin:<pass> -X PUT http://localhost:5984/_users/org.couchdb.user:eiri -d '{"name": "test", "password": "test", "roles": [], "type": "user"}'
However I always get
{
"error": "unknown_error",
"reason": "undefined"
}
The logs don't tell me much either
-------- rexi_server: from: couchdb#127.0.0.1(<0.4835.0>) mfa: fabric_rpc:update_docs/3 exit:{timeout,{gen_server,call,[couch_proc_manager,{get_proc,{doc,<<"_design/_auth">>,{1,[<<117,58,224,21,122,139,26,34,51,159,60,14,244,241,191,25>>]},{[{<<"language">>,<<"javascript">>},{<<"validate_doc_update">>,<<"\n function(newDoc, oldDoc, userCtx, secObj) {\n if (newDoc._deleted === true) {\n // allow deletes by admins and matching users\n // without checking the other fields\n if ((userCtx.roles.indexOf('_admin') !== -1) ||\n (userCtx.name == oldDoc.name)) {\n return;\n } else {\n throw({forbidden: 'Only admins may delete other user docs.'});\n }\n }\n\n if (newDoc.type !== 'user') {\n throw({forbidden : 'doc.type must be user'});\n } // we only allow user docs for now\n\n if (!newDoc.name) {\n throw({forbidden: 'doc.name is required'});\n }\n\n if (!newDoc.roles) {\n throw({forbidden: 'doc.roles must exist'});\n }\n\n if (!isArray(newDoc.roles)) {\n throw({forbidden: 'doc.roles must be an array'});\n }\n\n for (var idx = 0; idx < newDoc.roles.length; idx++) {\n if (typeof newDoc.roles[idx] !== 'string') {\n throw({forbidden: 'doc.roles can only contain strings'});\n }\n }\n\n if (newDoc._id !== ('org.couchdb.user:' + newDoc.name)) {\n throw({\n forbidden: 'Doc ID must be of the form org.couchdb.user:name'\n });\n }\n\n if (oldDoc) { // validate all updates\n if (oldDoc.name !== newDoc.name) {\n throw({forbidden: 'Usernames can not be changed.'});\n }\n }\n\n if (newDoc.password_sha && !newDoc.salt) {\n throw({\n forbidden: 'Users with password_sha must have a salt.' +\n 'See /_utils/script/couch.js for example code.'\n });\n }\n\n var available_schemes = ["simple", "pbkdf2", "bcrypt"];\n if (newDoc.password_scheme\n && available_schemes.indexOf(newDoc.password_scheme) == -1) {\n throw({\n forbidden: 'Password scheme ' + newDoc.password_scheme\n + ' not supported.'\n });\n }\n\n if (newDoc.password_scheme === "pbkdf2") {\n if (typeof(newDoc.iterations) !== "number") {\n throw({forbidden: "iterations must be a number."});\n }\n if (typeof(newDoc.derived_key) !== "string") {\n throw({forbidden: "derived_key must be a string."});\n }\n }\n\n var is_server_or_database_admin = function(userCtx, secObj) {\n // see if the user is a server admin\n if(userCtx.roles.indexOf('_admin') !== -1) {\n return true; // a server admin\n }\n\n // see if the user a database admin specified by name\n if(secObj && secObj.admins && secObj.admins.names) {\n if(secObj.admins.names.indexOf(userCtx.name) !== -1) {\n return true; // database admin\n }\n }\n\n // see if the user a database admin specified by role\n if(secObj && secObj.admins && secObj.admins.roles) {\n var db_roles = secObj.admins.roles;\n for(var idx = 0; idx < userCtx.roles.length; idx++) {\n var user_role = userCtx.roles[idx];\n if(db_roles.indexOf(user_role) !== -1) {\n return true; // role matches!\n }\n }\n }\n\n return false; // default to no admin\n }\n\n if (!is_server_or_database_admin(userCtx, secObj)) {\n if (oldDoc) { // validate non-admin updates\n if (userCtx.name !== newDoc.name) {\n throw({\n forbidden: 'You may only update your own user document.'\n });\n }\n // validate role updates\n var oldRoles = (oldDoc.roles || []).sort();\n var newRoles = newDoc.roles.sort();\n\n if (oldRoles.length !== newRoles.length) {\n throw({forbidden: 'Only _admin may edit roles'});\n }\n\n for (var i = 0; i < oldRoles.length; i++) {\n if (oldRoles[i] !== newRoles[i]) {\n throw({forbidden: 'Only _admin may edit roles'});\n }\n }\n } else if (newDoc.roles.length > 0) {\n throw({forbidden: 'Only admin may set roles'});\n }\n }\n\n // no system roles in users db\n for (var i = 0; i < newDoc.roles.length; i++) {\n if (newDoc.roles[i] !== 'metrics') {\n if (newDoc.roles[i][0] === '') {\n throw({\n forbidden:\n 'No system roles (starting with underscore) in users db.'\n });\n }\n }\n }\n\n // no system names as names\n if (newDoc.name[0] === '') {\n throw({forbidden: 'Username may not start with underscore.'});\n }\n\n var badUserNameChars = [':'];\n\n for (var i = 0; i < badUserNameChars.length; i++) {\n if (newDoc.name.indexOf(badUserNameChars[i]) >= 0) {\n throw({forbidden: 'Character ' + badUserNameChars[i] +\n ' is not allowed in usernames.'});\n }\n }\n }\n">>}]},[],false,[]},{<<"_design/_auth">>,<<"1-753ae0157a8b1a22339f3c0ef4f1bf19">>}},5000]}} [{gen_server,call,3,[{file,"gen_server.erl"},{line,223}]},{couch_query_servers,get_ddoc_process,2,[{file,"src/couch_query_servers.erl"},{line,614}]},{couch_query_servers,with_ddoc_proc,2,[{file,"src/couch_query_servers.erl"},{line,513}]},{couch_query_servers,validate_doc_update,5,[{file,"src/couch_query_servers.erl"},{line,368}]},{couch_db,'-validate_doc_update_int/3-lc$^0/1-0-',5,[{file,"src/couch_db.erl"},{line,903}]},{couch_db,'-validate_doc_update_int/3-fun-1-',3,[{file,"src/couch_db.erl"},{line,906}]},{couch_stats,update_histogram,2,[{file,"src/couch_stats.erl"},{line,102}]},{couch_db,'-prep_and_validate_updates/6-fun-1-',3,[{file,"src/couch_db.erl"},{line,1003}]}]
Any ideas what I'm doing wrong?

Related

how to write native query for different filter operations nodejs?

I want to add different filter operations based on values got in the query params. I have added all possible filters in operator attribute in route file. I dont have idea about mongodb queries,can add native queries as well if it possible. could you someone help?
route.js:
method: 'GET',
config: {
handler: controller.getFamilies,
cors: true,
validate: {
// headers: Joi.object({
// authorization: Joi.string().required(),
// }).unknown(),
query: Joi.object({
search: Joi.string().allow('').description('Search the family by name'),
offset: Joi.number().required().description('Specify the offset for the pagination'),
limit: Joi.number().required().description('Limit the number of records to be displayed in the result'),
filter_by: Joi.string().valid('Family','No of products', 'State', 'Completeness', 'Last updated').allow('').description('Filter the results from the specified filter value.'),
operator: Joi.string().valid('is empty', 'is not empty', 'contains', 'doesnt contain', 'starts with', 'ends with', 'Active', 'Inactive', '=', '!=', '>','>=', '<', '<=',' Total', 'Mandatory', 'Completeness', 'Custom','This week', 'Last week', 'Last 2 week', 'This month', 'Last Month', 'Last 2 Month').allow('').description('Provide operator name'),
from_value: Joi.string().allow('').description('from_value'),
to_value: Joi.string().allow('').description('to_value')
}).label('family'),
options: {
allowUnknown: true,
abortEarly: false,
},
failAction: async (request, h, err) => {
throw err;
},
},
},
controller.js:
if(!!filter_by){
const operator = req.query.operator
const from_value = req.query.from_value
const to_value = req.query.to_value
if(filter_by === 'Family'){
if(operator === 'contains'){ // familyname contains with some string
dbFamilies = await Family.find({client_id : client_id, status:!='ACTIVE', name: search})
}else if(operator === 'doesnt contain'){
}else if(operator === 'starts with'){
}else if(operator === 'ends with'){
}
}else if(filter_by === 'No of products'){
if(operator === '='){
}else if(operator === '!='){
}else if(operator === '>='){
}else if(operator === '>'){
}else if(operator === '<='){
}else if(operator === '<'){
}
}

DiscordAPIError: Invalid Form Body embeds[0].description: This field is required in discord.js v13

So I'm coding this discord bot using discord.js v13 right and it was all going fine until for some reason I started to get this runtime error concerning my embed's description.
What happens is that 3 buttons work, except for the last one (the one that's supposed to go to the very last page).
Here is my code:
import {
Interaction,
Message,
MessageActionRow,
MessageButton,
MessageEmbed,
} from 'discord.js'
import { ICommand } from 'wokcommands'
export default {
category: 'Testing',
description: 'Creates an embed pagination',
slash: 'both',
testOnly: true,
callback: async ({ user, message, interaction, channel }) => {
const embeds: MessageEmbed[] = []
const pages = {} as { [key: string]: number }
for (let i = 0; i < 4; ++i) {
embeds.push(new MessageEmbed()
.setDescription(`Testing page ${i + 1}`))
}
const getRow = (id: string) => {
const row = new MessageActionRow()
row.addComponents(
new MessageButton()
.setCustomId('first_embed')
.setStyle('SECONDARY')
.setEmoji('⏮')
.setDisabled(pages[id] === 0)
)
row.addComponents(
new MessageButton()
.setCustomId('prev_embed')
.setStyle('SECONDARY')
.setEmoji('⏪')
.setDisabled(pages[id] === 0)
)
row.addComponents(
new MessageButton()
.setCustomId('next_embed')
.setStyle('SECONDARY')
.setEmoji('⏩')
.setDisabled(pages[id] === embeds.length - 1)
)
row.addComponents(
new MessageButton()
.setCustomId('last_embed')
.setStyle('SECONDARY')
.setEmoji('⏭')
.setDisabled(pages[id] === embeds.length - 1)
)
return row
}
const id = user.id
pages[id] = pages[id] || 0
const embed = embeds[pages[id]]
let reply: Message | undefined
let collector
const filter = (i: Interaction) => i.user.id === user.id
const time = 1000 * 60 * 5
if (message) {
reply = await message.reply({
embeds: [embed],
components: [getRow(id)],
})
collector = reply.createMessageComponentCollector({ filter, time })
} else {
interaction.reply({
ephemeral: true,
embeds: [embed],
components: [getRow(id)],
})
collector = channel.createMessageComponentCollector({ filter, time })
}
collector.on('collect', (btnInt) => {
if (!btnInt) {
return
}
btnInt.deferUpdate()
if (btnInt.customId !== 'prev_embed' && btnInt.customId !== 'next_embed' && btnInt.customId !== 'first_embed' && btnInt.customId !== 'last_embed') {
return
}
console.log(embeds.length - 1)
console.log(embeds.length)
console.log(pages[id])
if (btnInt.customId === 'prev_embed' && pages[id] > 0) {
--pages[id]
} else if (btnInt.customId === 'next_embed' && pages[id] < embeds.length - 1) {
++pages[id]
} else if (btnInt.customId === 'first_embed' && pages[id] > 0) {
pages[id] = 0
} else if (btnInt.customId === 'last_embed' && pages[id] < embeds.length - 1) {
pages[id] = embeds.length
}
if (reply) {
reply.edit({
embeds: [embeds[pages[id]]],
components: [getRow(id)]
})
} else {
interaction.editReply({
embeds: [embeds[pages[id]]],
components: [getRow(id)]
})
}
})
}
} as ICommand
Error: DiscordAPIError: Invalid Form Body embeds[0].description: This field is required
The bot breaks as soon as I click the "last" button...
// ...
} else if (btnInt.customId === 'last_embed' && pages[id] < embeds.length - 1) {
pages[id] = embeds.length
}
// ...
This code appears to be setting the page to embeds.length. You probably want to set the page to embeds.length - 1, since arrays are zero-indexed.

Cannot send more than a few SendGrid emails with Node.js & Firebase

I am working on an app using Node.js as a backend on Firebase Functions and SendGrid to send mass emails to customers at one time when the tracking for a bulk of orders is ready. I can upload a CSV to the system with a list of 1-3, maybe a few more and will all work properly, but when I have as many as 100 or 1000, the system will not send and I get the error:
Error with sending tracking emails: Error: socket hang up followed by Error setting processed to true: Error: 4 DEADLINE_EXCEEDED: Deadline exceeded on Firebase Function's Node.js server logs. Is there a maximum for sending emails in bulk like this? Any clues on where the issue may lie?
Relevant Code Snippet:
"use strict";
import functions = require('firebase-functions');
import * as uuid from 'uuid';
import admin = require("firebase-admin");
import { DocumentSnapshot } from '#google-cloud/firestore';
import { RESPONSE_TYPES } from './common';
admin.initializeApp(functions.config().firebase);
const request = require("request");
const Papa = require('papaparse');
const sgMail = require('#sendgrid/mail')
sgMail.setApiKey(functions.config().sendgrid_api.key)
export const onUploadCreated = functions.firestore.document('users/{userId}/uploads/{uploadId}')
.onCreate(async (snap: DocumentSnapshot, context: functions.EventContext) => {
const newValue = snap.data();
if (newValue === null || newValue === undefined) {
return;
}
try {
const allPromises: Array<Promise<any>> = [];
console.log("Upload type set to set shipment tracking info...")
const orders: any = [];
// Parse through uploaded CSV
const options = {
download: true,
header: true,
worker: true
};
const parseStream = await Papa.parse(Papa.NODE_STREAM_INPUT, options);
allPromises.push(parseStream);
const dataStream = await request.get(newValue.fileUrl).pipe(parseStream);
allPromises.push(dataStream);
let parseError = "";
allPromises.push(
await parseStream.on("data", async (chunk: any) => {
if(
chunk['Order ID'] && chunk['Order ID'] !== undefined && chunk['Order ID'] !== null &&
chunk['Order Number'] && chunk['Order Number'] !== undefined && chunk['Order Number'] !== null &&
chunk['First Name'] && chunk['First Name'] !== undefined && chunk['First Name'] !== null &&
chunk['Last Name'] && chunk['Last Name'] !== undefined && chunk['Last Name'] !== null &&
chunk['Email'] && chunk['Email'] !== undefined && chunk['Email'] !== null &&
chunk['Tracking'] && chunk['Tracking'] !== undefined && chunk['Tracking'] !== null
){
// If in e notation, throw error
if(/e\+|E\+/g.test(chunk['Tracking'])){
parseError = "Tracking is in E notation, please reformat as outlined in NOTE 3 and re-upload."
} else {
await orders.push({
id: chunk['Order ID'],
number: chunk['Order Number'],
productName: chunk['Product Name'] || '',
productSize: chunk['Product Size'] || '',
productVariant: chunk['Product Variant'] || '',
firstName: chunk['First Name'],
lastName: chunk['Last Name'],
email: chunk['Email'],
tracking: chunk['Tracking'],
carrier: chunk['Carrier'] || '',
method: chunk['Method'] || '',
})
}
} else {
parseError = "Required columns were not properly defined properly. Please check your file follows the NOTES and re-upload."
}
})
);
// TODO: if multiple products, we need to just create more rows for each product like BS does
allPromises.push(
await dataStream.on("finish", async () => {
if(orders.length > 1000){
parseError = "You uploaded over 1000 orders to this upload. Please retry by only submitting 1000 orders at a time so we don't overwhelm the servers!"
}
if(parseError.length === 0){
console.log("Parsing complete, no errors. Number of orders processed: " + orders.length + ". Starting to add orders to Firestore Order docs now... ")
const emailMessages: { to: any; from: string; subject: string; text: string; html: string; }[] = [];
let shopData: FirebaseFirestore.DocumentData | any = null;
await admin.firestore().collection('public').doc("shop").get().then((shopDoc) => {
if (shopDoc.exists) {
let docWithMore = Object.assign({}, shopDoc.data());
docWithMore.id = shopDoc.id;
shopData = docWithMore;
} else {
console.error("Shop doc doesn't exist!")
}
}).catch((error) => {
console.log("Error getting shop document:", error);
})
const batchArray: any = [];
batchArray.push(admin.firestore().batch());
console.log("batchArray.length: " + batchArray.length);
let operationCounter = 0;
let batchIndex = 0;
let ordersProcessed = 0;
allPromises.push(
await orders.forEach((order: any) => {
const orderRef = admin.firestore().collection('orders').doc(order.id)
batchArray[batchIndex].set(
orderRef,
{
shipment: {
tracking: order.tracking,
carrier: order.carrier || '',
method: order.method || ''
}
},
{ merge: true }
);
const htmlEmail =
`
<div style="width: 100%; font-family: Arial, Helvetica, sans-serif">
${shopData?.nav?.showLogo ?
`
<div style="text-align: center;">
<img
alt="company logo"
src="${shopData?.logoUrl}"
width="${shopData?.nav?.logoSize || "200"}" height="auto"
/>
</div>
`
: ""
}
${shopData?.nav?.showTitle ? `<h1 style="margin: 20px 0 0 0; text-align: center;">${shopData?.name}</h1>` : ""}
<div style="margin: auto; width: 70%; padding: 1%;">
<h2>Great news ${order.firstName}!</h2>
<p>
Your order #${order.number} has shipped with the tracking # <b>${order.tracking}</b>.
</p>
${order?.productName ? `<p><b>Product Name:</b> ${order.productName}</p>` : ""}
${order?.productSize ? `<p><b>Product Size:</b> ${order.productSize}</p>` : ""}
${order?.productVariant ? `<p><b>Product Variant:</b> ${order.productVariant}</p>` : ""}
${order?.method ? `<p><b>Shipping Method:</b> ${order.method}</p>` : ""}
${order?.carrier ? `<p><b>Shipping Carrier:</b> ${order.carrier}</p>` : ""}
${shopData?.emails?.order?.shipment ?? ''}
<p>
Feel free to reach out to ${shopData?.emails?.support ?? `help#email.com`} if you have any questions!
</p>
</div>
</div>
`
const msg = {
to: order.email,
from: `noreply#email.com`,
subject: `${shopData?.name} Order #${order.number} Shipped!`,
text: `
Great news! We shipped out your order with the tracking #${order.tracking}!
${order?.method ? ` Shipping Method: ${order.method} ` : ""}
${order?.carrier ? ` Shipping Carrier: ${order.carrier} ` : ""}
`,
html: htmlEmail,
}
emailMessages.push(msg)
operationCounter++;
ordersProcessed++;
if (operationCounter === 499) {
console.log("operationCounter: " + operationCounter)
batchArray.push(admin.firestore().batch());
batchIndex++;
operationCounter = 0;
}
})
);
// Attempt to send out emails, if it fails, do not continue with adding changes to Firestore! Sending email is the main purpose of this process.
allPromises.push(
sgMail.send(emailMessages).then(async () => {
console.log(`${emailMessages.length} tracking emails sent successfully!`);
console.log("Starting to push batchArray.length: " + batchArray.length);
let batchesProcessed = 0;
allPromises.push(
await batchArray.forEach(async (batch: any) => {
console.log("batchesProcessed: " + batchesProcessed);
batchesProcessed++;
allPromises.push(await batch.commit())
if(batchesProcessed === batchArray.length){
console.log("Done pushing " + batchesProcessed + " batches!");
allPromises.push(
admin.firestore().collection('users').doc(context.params.userId).collection('uploads').doc(context.params.uploadId).set({
processed: true,
response: {
type: RESPONSE_TYPES.SUCCESS,
message: `Successfully sent ${emailMessages.length} tracking emails and updated ${ordersProcessed} orders on the database with this tracking info.`,
read: false,
timestamp: Date.now()
}
}, { merge: true }).then(() => {
console.log("Set upload processed to true.")
})
);
}
})
);
}).catch((error: any) => {
console.log("Error with sending tracking emails: " + error);
allPromises.push(
admin.firestore().collection('users').doc(context.params.userId).collection('uploads').doc(context.params.uploadId).set({
processed: true,
response: {
type: RESPONSE_TYPES.ERROR,
message: `Error sending tracking emails: ${error}`,
read: false,
timestamp: Date.now()
}
}, { merge: true }).then(() => {
console.log("Set upload processed to true.")
}).catch(err => {
console.error("Error setting processed to true: " + err)
})
);
})
)
} else {
console.error("Error parsing shipments CSV: " + parseError)
allPromises.push(
admin.firestore().collection('users').doc(context.params.userId).collection('uploads').doc(context.params.uploadId).set({
processed: true,
response: {
type: RESPONSE_TYPES.ERROR,
message: `Error parsing shipments CSV: ${parseError}`,
read: false,
timestamp: Date.now()
}
}, { merge: true }).then(() => {
console.log("Set upload processed to true and parseError")
}).catch(error => {
console.error("Error setting processed to true and parseError: " + error)
})
);
}
})
);
return Promise.all(allPromises)
} catch(error) {
console.error("Error: " + error);
return;
}
});
The error you get has nothing to do with Firebase. This is a SendGrid limit for Free Accounts, as you can see on this page.
The limit is 100 emails per day. To send more emails you'll need to upgrade your account to a paid one.

Multiplication with 2 objects that may have a variation in one label in NodeJS / Express

I have 2 objects:
totalGridConso [
{ horoseason: 'HPH', value: 0 },
{ horoseason: 'HCH', value: 0 },
{ horoseason: 'HPE', value: 3498.54 },
{ horoseason: 'HCE', value: 4460.99 }
]
billedkWh [
{ horoseason: 'BASE', value: NaN },
{ horoseason: 'HPH', value: 8.4370003 },
{ horoseason: 'HCH', value: 5.5939999 },
{ horoseason: 'HPB', value: 5.0570002 },
{ horoseason: 'HCB', value: 3.3499999 }
]
What is important to mention is that I can match HPE with HPB and HCE with HCB, it is like old label, and new label, but the value is the same.
Now, I would like to make
const HTVar = billedkWh.map(x => {
const HTVal = totalGridConso.find(it => hasSameHoroseason(it, x))
return {
horoseason: x.horoseason,
value: typeof HTVal != 'undefined' ? HTVal.value * x.value / 100 : null
}
})
So I can get something similar to :
const HTVar = [
{horoseason: "BASE", value: totalGridConso.find(it => it.horoseason === "BASE").value * billedKWh.find(it => it.horoseason === "BASE") / 100},
{horoseason: "HPH", value: totalGridConso.find(it => it.horoseason === "HPH").value * billedKWh.find(it => it.horoseason === "HPH") / 100},
{horoseason: "HCH", value: totalGridConso.find(it => it.horoseason === "HCH").value * billedKWh.find(it => it.horoseason === "HCH") / 100},
{horoseason: "HPB", value: totalGridConso.find(it => it.horoseason === "HPE").value * billedKWh.find(it => it.horoseason === "HPB") / 100},
{horoseason: "HCB", value: totalGridConso.find(it => it.horoseason === "HCE").value * billedKWh.find(it => it.horoseason === "HCB") / 100},
]
with hasSameHoroseason(it, x)
function hasSameHoroseason(it, x) {
// In files, sometime the we find HPB, sometimes we find HPE
// In files, sometime the we find HCB, sometimes we find HCE
return it.horoseason === x.horoseason
|| (it.horoseason === "HPE" && it.horoseason === "HPB")
|| (it.horoseason === "HPB" && it.horoseason === "HPE")
|| (it.horoseason === "HCE" && it.horoseason === "HCB")
|| (it.horoseason === "HCB" && it.horoseason === "HCE")
;
}
The result should be
result [
{ horoseason: 'BASE', value: null },
{ horoseason: 'HPH', value: 0 },
{ horoseason: 'HCH', value: 0 },
{ horoseason: 'HPE', value: 176.92 }, // 3498.54×5.057÷100
{ horoseason: 'HCE', value: 14944.2718901 } // 4460.99×3,34999÷100
]
But I get:
undefined
{ horoseason: 'HPH', value: 0 }
{ horoseason: 'HCH', value: 0 }
undefined
undefined
What am I missing ?
The problem is with this code
return it.horoseason === x.horoseason
|| (it.horoseason === "HPE" && it.horoseason === "HPB")
|| (it.horoseason === "HPB" && it.horoseason === "HPE")
|| (it.horoseason === "HCE" && it.horoseason === "HCB")
|| (it.horoseason === "HCB" && it.horoseason === "HCE")
When you try to match different values you are actually checking the property on it both times, so the code tries to check if it.horoseason is equal to both "HPE" and to "HPB" at the same time.
The correct logic is to check both objects:
return it.horoseason === x.horoseason
|| (it.horoseason === "HPE" && x.horoseason === "HPB")
|| (it.horoseason === "HPB" && x.horoseason === "HPE")
|| (it.horoseason === "HCE" && x.horoseason === "HCB")
|| (it.horoseason === "HCB" && x.horoseason === "HCE")
If you need to add more cases in the future, you can improve this by adding a table for equalities. That would help as it's easier to maintain:
function hasSameHoroseason(it, x) {
const commonHPE_HPB = Symbol("HPE_HPB");
const commonHCE_HCB = Symbol("HCE_HPB");
const tableOfEqualities = {
"HPE": commonHPE_HPB,
"HPB": commonHPE_HPB,
"HCE": commonHCE_HCB,
"HCB": commonHCE_HCB
}
const itNormalised = tableOfEqualities[it.horoseason] || it.horoseason;
const xNormalised = tableOfEqualities[x.horoseason] || x.horoseason;
return itNormalised === xNormalised;
}
test("foo", "bar") //false
test("HPE", "bar") //false
test("HPE", "HPE") //true
test("HPE", "HPB") //true
test("HCE", "HCB") //true
test("HPE", "HCB") //false
function test(a, b) {
console.log(`Values:
"${a}"
"${b}"
considered equal:`,
hasSameHoroseason(
{ horoseason: a },
{ horoseason: b }
)
)
}
The normalisation step will map HPE and HPB to the same value, so they can be directly converted. Same with HCE/HCB. I've used a Symbol here as it is quite appropriate, you will never get clashes with other values that way.
You can also extract the normalisation logic as well as the table of values separately and you only need to add items to the table that way, you never need to change the actual algorithm.
const commonHPE_HPB = Symbol("HPE_HPB");
const commonHCE_HCB = Symbol("HCE_HPB");
const tableOfEqualities = {
"HPE": commonHPE_HPB,
"HPB": commonHPE_HPB,
"HCE": commonHCE_HCB,
"HCB": commonHCE_HCB
}
function normalise(val) {
return tableOfEqualities[val] || val;
}
function hasSameHoroseason(it, x) {
const itNormalised = normalise(it.horoseason);
const xNormalised = normalise(x.horoseason);
return itNormalised === xNormalised;
}
test("foo", "bar") //false
test("HPE", "bar") //false
test("HPE", "HPE") //true
test("HPE", "HPB") //true
test("HCE", "HCB") //true
test("HPE", "HCB") //false
function test(a, b) {
console.log(`Values:
"${a}"
"${b}"
considered equal:`,
hasSameHoroseason(
{ horoseason: a },
{ horoseason: b }
)
)
}

Amazon Alexa's in skill purchasing upsell is not working

I am implementing isp and I found that my upsell message is not working in the log it shows upsell message not defined.
Here is a piece of code I tried:
const HintIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'HintIntent';
},
handle(handlerInput) {
const locale = handlerInput.requestEnvelope.request.locale;
const ms = handlerInput.serviceClientFactory.getMonetizationServiceClient();
// Determine if the customer has purchased the hint_pack
return ms.getInSkillProducts(locale).then(function(res) {
var product = res.inSkillProducts.filter(record => record.referenceName === 'hint_pack');
if (isEntitled(product)) {
return handlerInput.responseBuilder
.speak(speechText)
.reprompt(sessionAttributes.currentRiddle.question)
.withSimpleCard('Level Up Riddles', speechText)
.getResponse();
} else {
const upsellMessage = "You don't currently own the hint pack. Want to learn more about it?";
return handlerInput.responseBuilder
.addDirective({
'type': 'Connections.SendRequest',
'name': 'Upsell',
'payload': {
'InSkillProduct': {
'productId': product[0].productId
},
'upsellMessage': upsellMessage
},
'token': 'correlationToken'
})
.getResponse();
}
});
}
};
and I am using auto delegation for other purposes.
here is the error
"name": "Upsell",
"payload": {
"purchaseResult": "ERROR",
"productId": "amzn1.adg.product.214ce17e-f466-4b08-9afa-215954c297ac",
"message": "Upsell was not presented."
},
"token": "correlationToken"

Resources