The original error say's: Cannot destructure property 'firstime' of 'undefined' or 'null'.
I am developing web-base desktop application for Windows pc using node.js and Electron.
I am trying to persist some data in user data directory, I found the idea and using the same approach in this link.
Writing and fetching data works fine, however the error occurred at the first time of fetching the data.
here is the code for UserPreferences class
const electron = require('electron');
const path = require('path');
const fs = require('fs');
class UserPreferences {
constructor(opts) {
const userDataPath = (electron.app || electron.remote.app).getPath('userData');
this.path = path.join(userDataPath, opts.configName + '.json');
this.data = parseDataFile(this.path, opts.defaults);
console.log(userDataPath);
}
get(key) {
return this.data[key];
}
set(key, val) {
this.data[key] = val;
fs.writeFileSync(this.path, JSON.stringify(this.data));
}
}
function parseDataFile(filePath, defaults) {
try {
return JSON.parse(fs.readFileSync(filePath));
} catch (error) {
return defaults;
}
}
module.exports = UserPreferences;
and here's the function for using the UserPreferences class
function isFirstTime() {
try{
const userAccount = new UserPreferences({
configName: 'fipes-user-preferences', // We'll call our data file 'user-preferences'
defaults: {
user: { firstime: true, accountid: 0, profileid: '' }
}
});
var { firstime, accountid, profileid } = userAccount.get('user');
if (firstime === true) { //check if firstime of running application
//do something
} else {
//do something
}
}catch(err){
console.log(err.message);
}
}
the error occurred on the line where I am checking weather the firstime is true or false.
First of all do not declare a object like var { firstTime, .. } like this. if you do this firstTime will be a property of an anonymous object. That you can never access elsewhere. Check what is the output of userAccount.get('user') function, output contain some object like { firstime: true, accountid: "test", profileid: "test" } then try this. Hope this helps you.
var result=userAccount.get('user');
if(result.firstTime===true){
//your code
}
Here is a version of UserPreferences which will be more natural to use as you write your code. You can create it like you see in isFirstTime.
console.debug(userPreferences[accountId]);
userPreferences[accountId] = 1;
This is preferred because there is no reason for a developer not to treat UserPreferences as an object. Another good idea would be separating the writing to the file into a separate flush method, in case you are updating preferences often.
const electron = require("electron");
const fs = require("fs");
const path = require("path");
class UserPreferences {
constructor(defaultPrefs, pathToPrefs) {
const app = electron.app || electron.remote.app;
this.pathToPrefs = path.join(app.getPath("userData"), pathToPrefs + ".json");
try {
this.store = require(this.pathToPrefs);
}
catch (error) {
this.store = defaultPrefs;
}
return new Proxy(this, {
get(target, property) {
return target.store[property];
},
set(target, property, value) {
target.store[property] = value;
fs.writeFileSync(target.pathToPrefs, JSON.stringify(target.store));
}
});
}
}
module.exports = UserPreferences;
Here is a pure version of isFirstTime, that should do what you want, while maintaining a more robust method of checking for isFirstTime. The check can also be changed so check whether lastSignIn is equal to createdAt (with appropriate defaults, of course).
function isFirstTime() {
const account = new UserPreferences({
user: {
accountId: 0,
createdAt: new Date(),
lastSignIn: null,
profileId: ""
}
}, "fipes-user-preferences");
const {lastSignIn} = account;
return lastSignIn === null;
}
Related
I'm using React Native
I'm staying on the same issue over and over again.
This code uses axios. I don't know why it keeps returning undefined. I wrote all of my twilio ids correctly.
I used serverless twilio it worked but I didn't know how I could change it my way.
If I can't use this code anymore, please let me know if there is another good way.
I don't know how to use node.js.
I'd appreciate your help.
Here is the code.
Fetch.js
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true,
});
exports.default = void 0;
var _querystring = _interopRequireDefault(require('querystring'));
var _axios = _interopRequireDefault(require('axios'));
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {default: obj};
}
class Fetch {
constructor({twilioServiceSid, twilioAccountSid, twilioAuthToken}) {
const fetch = _axios.default.create({
baseURL: `https://verify.twilio.com/v2/Services/${twilioServiceSid}`,
auth: {
username: twilioAccountSid,
password: twilioAuthToken,
},
});
this.fetch = fetch;
}
async post({path, payload}) {
try {
const obj = {
To: payload.phone,
Channel: 'sms',
};
if ('code' in payload) {
obj['Code'] = payload.code;
}
const {status, data} = await this.fetch.request({
method: 'POST',
url: path,
data: _querystring.default.stringify(obj),
});
return {
status,
data,
};
} catch (error) {
return error;
}
}
}
exports.default = Fetch;
Verify.js
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true,
});
exports.default = void 0;
var _phone = _interopRequireDefault(require('phone'));
var _fetch = _interopRequireDefault(require('./fetch'));
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {default: obj};
}
class Serify {
constructor({twilioServiceSid, twilioAccountSid, twilioAuthToken}) {
this.fetch = new _fetch.default({
twilioServiceSid,
twilioAccountSid,
twilioAuthToken,
});
this.twilioAccountSid = twilioAccountSid;
this.twilioAuthToken = twilioAuthToken;
}
async start({phone, country}) {
try {
const number = (0, _phone.default)(phone, country ? country : 'USA');
if (!number) {
throw new Error(
'Invalid phone number. Please provide the country code in an ISO-3166 alpha 3 format.',
);
}
const response = await this.fetch.post({
path: 'Verifications',
payload: {
phone: number,
},
});
return {
code: response.status,
data: response.data,
};
} catch (error) {
return new Error(error);
}
}
async verify({phone, country, code}) {
try {
const number = (0, _phone.default)(phone, country ? country : 'USA');
if (!number) {
throw new Error(
'Invalid phone number. Please provide the country code in an ISO-3166 alpha 3 format.',
);
}
if (code.length <= 3) {
throw new Error('Invalid SMS code.');
}
const response = await this.fetch.post({
path: 'VerificationCheck',
payload: {
phone: number,
code: code.toString(),
},
});
return {
code: response.status,
data: response.data,
};
} catch (error) {
return new Error(error);
}
}
}
exports.default = Serify;
const verify = new Serify({
twilioServiceSid: '',
twilioAccountSid: '',
twilioAuthToken: '',
});
try {
const ss = await verify.start({
phone: '+1456123123',
});
console.log(ss);
} catch (e) {
console.log(e);
}
Twilio developer evangelist here.
I'm not sure which part you are saying is returning undefined. But, there is a bigger issue here.
When building a native application, you cannot embed your Twilio credentials within the app and make API requests direct to Twilio from the application. A malicious user could decompile the application, extract your account credentials and abuse your Twilio account.
You should not make the API request from your application. Instead, you should build a server side proxy that stores your Twilio credentials and makes the API request, which you trigger from the application.
I don't have an example of this for Twilio Verify and React Native, but the theory is the same as sending an SMS from a React application.
If you had managed to implement this using Twilio Serverless, then you should go back to that version. If you have trouble with that, I would ask a new question.
I'm working with class 'State', it has a model and controller file. However, when importing these classes on my main (app.js), there seem to be a clash as 'state.model.js' is 'required' both in app.js and state.controller.js. Thus, giving me the error, State is not a constructor.
app.js:
const State = require('./model/state.model');
const stateController = require('./controller/state.controller');
stateController.insert(msg, State.STATE_REMINDER.name, State.STATE_REMINDER.key.day, selected_day, (err, doc) => {
if (err) throw err;
console.log("[STATE][INSERT]", doc);
});
state.controller.js:
const Database = require('../database');
const State = require('../model/state.model');
const db = Database.collection('states');
db.loadDatabase((err) => {
if (err) throw err;
console.log("[STATES] Database connected");
});
exports.insert = (msg, state, key, value, callback) => { // msg refers to Telegram Callback
let insertState = new State(undefined, msg.from.id, msg.chat.id, state, key, value);
console.log(insertState);
db.insert(insertState, (err, newDoc) => {
if (err) throw err;
callback(err, newDoc);
});
};
state.model.js:
const stateController = require('../controller/state.controller');
const STATE_REMINDER = {
name: "STATE_REMINDER",
key: {
day: "DAY",
time: "TIME"
}
};
class State {
constructor(id, user_id, chat_id, state, key, value) {
this._id = id;
this.user_id = user_id;
this.chat_id = chat_id;
this.state = state;
this.key = key;
this.value = value;
this.timestamp = new Date();
}
static get STATE_REMINDER() {
return STATE_REMINDER;
}
}
module.exports = State;
This problem is actually fixed when i swap the order of codes in app.js to:
const stateController = require('./controller/state.controller');
const State = require('./model/state.model');
Why is that so? Any help is appreciated. Thanks!
Your code has a circular dependency loop:
app.js requires state.model.js which requires state.controller.js which requires state.model.js which requires state.controller.js etc etc
In this case, require will detect a circular dependency and will NOT go on forever. However this means at some point it will just return "null" instead of the actual class. It probably will look like this:
app.js -> requires state.model.js
state.model.js -> requires state.controller.js
state.controller.js -> requires state.model.js. At this point node detects the circular looop and will return null for state.model.js, then will begin to unwind the stack.
I'm not sure why it would work when you switch the order, the loop is still there and should be causing an issue still, probably some oddity in how node is importing the files.
I just started out with Node.js and AWS DynamoDB and I'm stuck with a very basic problem I believe. I'm looking for a way to return a boolean if a particular key exists in a table. So here's the code I have so far:
const AWS = require('aws-sdk')
const TOKEN_TABLE = process.env.TOKENS_TABLE
const dynamoDb = new AWS.DynamoDB.DocumentClient()
module.exports = {
isValid: function (token) {
const params = {
TableName: TOKEN_TABLE,
Key:
{
token: token
}
}
var exists = false
dynamoDb.get(params, (error, result) => {
if (result.Item)
exists = true
else
exists = false
})
return (exists)
}
}
When i call this function, the value of 'exists' never changes after it was declared even if the item I'm looking for is in the table. I've looked at similar questions and none of them could really help me out or a least explain why this occurs. Thanks
First, dynamoDb.get returns a promise. Therefore, you return 'exists' before your promise finishes and returns. What I've found to be the best way and cleanest way around this is to make your function async and await the return of the promise.
For example,
const AWS = require('aws-sdk')
const TOKEN_TABLE = process.env.TOKENS_TABLE
const dynamoDb = new AWS.DynamoDB.DocumentClient()
module.exports = {
isValid: async function (token) {
const params = {
TableName: TOKEN_TABLE,
Key:
{
token: token
},
AttributesToGet: [
'token'
]
}
var exists = false
let result = await dynamoDb.get(params).promise();
if (result.Item !== undefined && result.Item !== null) {
exists = true
}
return (exists)
}
}
So I'm creating a singleton class and when I require it from my server.js file it works fine, but when I require it from another file it returns as undefined. I'll try to post relevant code but some will have to be cut out due to work.
server.js
const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
const http = require('http');
const app = express();
const config = require('config');
const FBConfigsListener = require('./server/amq_listeners/fb_configs.listener');
const FBConfigs = require('./server/models/FBConfigs');
//Api file for interacting with mongodb
const api = require('./server/routes/api.routes');
//Parsers
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
//Angular Dist output folder
app.use(express.static(path.join(__dirname, 'dist')));
//Api location
app.use('/api', api);
//Send all other requests to angular
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'dist/index.html'));
});
//set port
var port = config.get('webserver.port');
app.set('port', port);
const server = http.createServer(app);
server.listen(port, () => console.log(`Running on localhost:${port}`));
models/FBConfigs.js
var ConfigModel = require('./config');
var config = require('config');
var _ = require('lodash');
var FBConfigsListener = require('../amq_listeners/fb_configs.listener');
var AMQAdapter = require('../adapters/amq.adapter');
var uniqid = require('uniqid');
const connectionOptions = config.get('activemq.connectionOptions');
class FBConfigs {
constructor() {
console.log(config.get('environments'));
this.listener = FBConfigsListener;
this.configs = {};
this.unique_keys = ['id'];
this.update_topic = '/topic/fusebuilder.update.config.';
console.log(FBConfigsListener);
//AMQ Client
this.amq_client = AMQAdapter.getInstance(connectionOptions.host, connectionOptions.port);
}
add(key, config) {
if (!(key in this.configs)) {
this.configs[key] = new ConfigModel(this.unique_keys);
}
this.configs[key].add(config);
}
get(key) {
let configs_json = {};
if (key) {
configs_json[key] = JSON.parse(this.configs[key].toString());
} else {
for (let key in this.configs) {
configs_json[key] = JSON.parse(this.configs[key].toString());
}
}
return configs_json;
}
updateByID(key, id, input_config) {
let configs = this.configs[key].get();
for (let config of configs) {
if (input_config.id === config.id) {
this.update(key, _.merge(config, input_config));
}
}
}
//Send update to config topic
update(key, config) {
let topic = this.update_topic + key;
var update_object = {};
if (Array.isArray(config)) {
update_object[key] = [...config];
} else {
update_object[key] = [config];
}
console.log(`Sending ${key} update:${JSON.stringify(update_object)}`);
this.amq_client.sendMessage(topic, update_object);
}
copyTo(key, id, env) {
let selected_env = config.get('environments.' + env);
// let tmp_amq_client = new AMQAdapter(selected_env.host, selected_env.port);
let selected_config = this.configs[key].getByID(id);
console.log(this);
if (key === 'fuses') {
console.log('In FBConfig Copy to for fuses');
const get_fuse_topic = '/topic/fusebuilder.get_fuse';
const tran_id = uniqid();
const sendObj = { fuseName: id, tran_id };
this.amq_client.sendMessage(get_fuse_topic, sendObj);
var startTime = process.hrtime()[0];
var timeout = false;
while (!this.listener.get_copy_fuse_data(tran_id)) {
console.log('Waiting for config');
sleep(100);
if (process.hrtime()[0] - startTime > 3) {
console.log('Timed out');
timeout = true;
break;
}
}
console.log(JSON.stringify(FBConfigsListener.get_copy_fuse_data(tran_id)));
} else {
tmp_amq_client.sendMessage(this.update_topic, selected_config);
}
console.log(`Copy ${key} id:${id} to ${env}`);
}
}
module.exports = new FBConfigs();
amq_listener/fb_configs.listener.js
const config = require('config');
var AMQAdapter = require('../adapters/amq.adapter');
var FBConfigs = require('../models/FBConfigs');
**removed for work**
class FBConfigsListener {
constructor() {
this.instance;
this.copy_fuse_data = {};
//AMQ Client
this.amq_client = AMQAdapter.getInstance(connectionOptions.host, connectionOptions.port);
//Subscribe to configs
this.amq_client.subscribe(config_subscribe_topic, this.config_topic_callback.bind(this));
//Request Tables
this.amq_client.sendMessage(config_request_topic, { tables: config_tables });
//Subscribe to Copy Fuse topic
this.amq_client.subscribe(subscribe_fuse_copy_topic, this.copy_fuse_callback.bind(this));
}
config_topic_callback(err, message) {
let dest = this.amq_client.getDestination(message);
let key = this.get_key_from_topic(dest);
this.amq_client.readMessage(message, body => {
let configs = JSON.parse(body);
if (key in configs) {
for (let config of configs[key]) {
FBConfigs.add(key, config);
}
}
});
}
copy_fuse_callback(err, message) {
this.amq_client.readMessage(message, body => {
const config = JSON.parse(body);
this.copy_fuse_data[config.tran_id] = config;
});
}
//Get Key from the topic and convert using key map if needed
get_key_from_topic(topic) {
let key = topic.split('.')[topic.split('.').length - 1];
key = key in key_map ? key_map[key] : key;
return key;
}
get_copy_fuse_data(id) {
if (id in this.copy_fuse_data) {
return this.copy_fuse_data[id];
} else {
return false;
}
}
}
module.exports = new FBConfigsListener();
Error happens in FBConfigs. FBConfigsListener returns {} so all functions in there are undefined. Even if I do console.log(require('../amq_listeners/fb_configs.listener')) it prints {} But doing the same thing in server.js (with updated path) it prints the module.
Also tips on how to improve my coding style would be appreciated too.
Edit
So I found out that I have a circular dependency between these classes. How can this be fixed while allowing me to call one from the other.
I would suggest you to instantiate your dependencies firstly and store them in some object which you can pass then to your dependent classes. The structure can be
factories/services.js
/*
* Instantiates passed services and passes injector object to them
*/
module.exports = function createServices(injector, services) {
return Object.entries(services)
.reduce((aggregator, [name, serv]) => {
const name_ = camelCase(name);
aggregator.set(name_, new serv(injector));
return aggregator;
}, new Map());
};
lib/service.js
/**
* Base class for classes need any injections
*/
module.exports = class Service {
constructor(injector) {
this.injector = injector;
}
get dependencies() {
return this.injector.dependencies;
}
/*
* Background jobs can be ran here
*/
async startService() {}
/*
* Background jobs can be stopped here
*/
async stopService() {}
};
lib/injector.js
const Service = require('./service');
/*
* Contains all dependencies
*/
module.exports = class Injector {
constructor() {
this.services = new Map();
this._dependencies = {};
}
has(name) {
return this.services.has(name);
}
register(name, service) {
if (this.has(name)) {
throw new Error(`Service ${name} already exists`);
}
if (service instanceof Service === false) {
throw new Error('Argument #2 should be an instance of Service');
}
this.services.set(name, service);
this._dependencies[name] = service;
}
unregister(name) {
if (! this.has(name)) {
throw new Error(`Service ${name} not found`);
}
this.services.delete(name);
delete this._dependencies[name];
}
get dependencies() {
return { ...this._dependencies };
}
/*
* Starts all registered services
*/
async start() {
for (let service of this.services.values()) {
await service.startService();
}
}
/*
* Stops all registered services
*/
async stop() {
for (let service of this.services.values()) {
await service.stopService();
}
}
};
Then import, initialize and bind your services in the main file (don't forget to export just a class, not an object like you do it now).
server.js
const createServices = require('./factories/services.js');
const injector = require('./lib/injector');
const Injector = new injector();
const services = createServices(Injector, [require('./server/amq_listeners/fb_configs.listener'), require('./server/models/FBConfigs')]);
services.forEach((service, name) => {
Injector.register(name, service);
});
// Start services
Injector.start();
Inherit required classes to Service class and you will get an access to all dependencies there (don't forget to call super() from constructor). Like
models/FBConfigs.js
const Service = require('../lib/service');
class FBConfigs extends Service {
constructor(injector) {
super(injector);
const { FBConfigsListener } = this.dependencies;
...your code here
}
async startService() {
...run bg job or init some connection
}
async stopService() {
...stop bg job or close some connection
}
}
module.exports = FBConfigs;
Also you can pass some config object to createServices (I didn't include it here) with keys equal to service names and values containing config object and pass config to appropriate service.
It is caused by that circular dependency. You should avoid it or used very carefully.
In your case the fix is probably pretty simple, move the line var FBConfigs = require('../models/FBConfigs'); from listener at the end of the file as the last line (yes, even after the module.exports).
Edit: Actually it maybe is not enough as I checked the code more in detail. As you are not using Listener in FBConfig constructor, you can create method assignListener, remove this.listener from that constructor and call it later in server.js which will do the this.listener
Or the last solution, which is also "best practice". Do not export the instances. Export the classes only. Then in server.js create these instances after both are required.
I have the following case, when deleting any data, I need to delete the app badges (at the moment I delete them using silent push notication and reduce the app badges number with the cloud function) if the user who sent the request has deleted. But since the user who deleted could send several requests to different users in different places, so I decided that I need to create a function that will be called in firebase database trigger functions and also it will help not to duplicate the same code everywhere .
The function will be approximate such
function adminRemoveAppBadge(userID, dataID, categoryID) {
};
And for example, call it in this function
module.exports = functions.database.ref('/cards/{cardID}/interestedUsers/{interestedUserID}').onWrite(event => {
const currentData = event.data.current;
const prevData = event.data.previous;
const cardID = event.params.cardID;
const interestedUserID = event.params.interestedUserID;
if (currentData.val() && !prevData.val()) {
// value created
return console.log('cardInterestedUserHandler - created');
} else if (!currentData.val() && prevData.val()) {
// value removed
console.log('cardInterestedUserHandler - removed', currentData.val());
const cardRef = admin.database().ref("cards").child(cardID);
const cardRefPromise = cardRef.once("value", function(snap, error) {
if (error) {
return error;
};
if (snap.val()) {
const cardJSON = snap.val();
const cardOwnerID = cardJSON["ownerID"];
if (cardOwnerID) {
const cardOwnerAppBadgesRef = admin.database().ref("userAppBadges").child(cardOwnerID).child("appBadgeModels").orderByChild("dataID").equalTo(cardID);
const cardOwnerAppBadgesRefPromise = cardOwnerAppBadgesRef.once("value", function (cardOwnerAppBadgesRefSnap, error) {
if (error) {
return error;
};
if (cardOwnerAppBadgesRefSnap.val()) {
var deletingPromises = [];
cardOwnerAppBadgesRefSnap.forEach(function(cardOwnerAppBadgesRefSnapChild) {
const appBadgeModelJSON = cardOwnerAppBadgesRefSnapChild.val();
const appBadgeModelID = appBadgeModelJSON["id"];
const senderID = appBadgeModelJSON["senderID"];
if (appBadgeModelID && senderID) {
if (senderID == interestedUserID) {
const cardOwnerAppBadgeRef = admin.database().ref("userAppBadges").child(cardOwnerID).child("appBadgeModels").child(cardOwnerAppBadgeModelID);
const cardOwnerAppBadgeRefPromise = cardOwnerAppBadgeRef.remove();
deletingPromises.push(cardOwnerAppBadgeRefPromise);
// to call
adminRemoveAppBadge
};
} else {
console.log("cardOwnerAppBadgeModelID == null");
};
});
return Promise.all(deletingPromises);
};
});
return Promise.all([cardOwnerAppBadgesRefPromise]);
} else {
return console.log("owner id == null");
};
};
});
return Promise.all([cardRefPromise]);
} else {
return console.log('cardInterestedUserHandler - updated');
};
});
Also functions are in different files. How can I call it in other firebase cloud functions and how do I deploy this function?
Update I tried to do so one of the options as written here and here, but when I tried to do deploy I got an error Cannot find module 'AppBadges/adminRemoveAppBadge.js'.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
exports.adminRemoveAppBadge = function (userID, dataID, categoryID) {
console.log("adminRemoveAppBadge nil");
};
Requested this function so
var adminRemoveAppBadgeModule = require("AppBadges/adminRemoveAppBadge.js");
and call this functions so
adminRemoveAppBadgeModule.adminRemoveAppBadge(cardOwnerID, cardID, 0);
Google Functions are just JS - so normal routes to include code work.
I place my "library" functions in a folder /lib
So my functions folder looks like this:
/functions
/lib
BuildImage.js
SendEmail.js
index.js
package.json
...etc...
within my index.js I just include my code:
const SendMail = require('./lib/SendMail')
const sendMail = new SendMail({
database: db,
mailgun: mailgun
})
exports.sendContactUsMessage = functions.database.ref('/contact-messages/{itemId}').onWrite(sendMail.send(event))
EDIT Added /lib/SendMail.js code:
module.exports = class SendMail {
constructor(config) {
if (!config) {
throw new Error ('config is empty. Must pass database and mailgun settings')
}
if (!config.database) {
throw new Error('config.database is empty. Must pass config.database')
}
if (!config.mailgun) {
throw 'config.mailgun is empty. Must pass config.mailgun'
}
this.database = config.database
this.mailgun = config.mailgun
}
sendEmail (emailData) {
return new Promise((resolve, reject) => {
this.mailgun.messages().send(emailData, (error, body) => {
if (error) {
if (debug) {
console.log(error)
}
reject(error)
} else {
if (debug) {
console.log(body)
}
resolve(body)
}
})
})
}
...
}