I plan to create a main tree named users which will include the name different users used as username. So, from each username will be included their data e.g. Full Name, Address, Phone No.
I want to know how to get each user's data when they log in on their profile.
First of all i suggest you spend some time getting familiar with firebase by reading the Firebase Guide (Link to old Firebase Guide). Everything you need to know to answer your own question is available there. But for simplicity i will put an example here:
Lets start with security, here are the basic firebase rules you need for this example: (source: Understanding Security) (old source: Understanding Security)
{
"rules": {
"users": {
"$user_id": {
".write": "$user_id === auth.uid"
}
}
}
}
I will skip the actual user creation and logging in and focus on the question about storing and retrieving user data.
Storing data: (source: Firebase Authentication) (old source: User Authentication)
// Get a reference to the database service
var database = firebase.database();
// save the user's profile into Firebase so we can list users,
// use them in Security and Firebase Rules, and show profiles
function writeUserData(userId, name, email, imageUrl) {
firebase.database().ref('users/' + userId).set({
username: name,
email: email
//some more user data
});
}
The resulting firebase data will look like this:
{
"users": {
"simplelogin:213": {
"username": "password",
"email": "bobtony"
},
"twitter:123": {
"username": "twitter",
"email": "Andrew Lee"
},
"facebook:456": {
"username": "facebook",
"email": "James Tamplin"
}
}
}
And last but not least the retreiving of the data, this can be done in several ways but for this example i'm gonna use a simple example from the firebase guide: (source: Read and Write data) (old source: Retreiving Data)
//Get the current userID
var userId = firebase.auth().currentUser.uid;
//Get the user data
return firebase.database().ref('/users/' + userId).once('value').then(function(snapshot) {
//Do something with your user data located in snapshot
});
EDIT: Added example of return data
So when you are logged in as user twitter:123 you will get a reference to the location based on your user id and will get this data:
"twitter:123": {
"username": "twitter",
"email": "Andrew Lee"
}
Though I agree with Andre about setting the rules for good security - I would handle the data a bit differently. Instead of generating the string I use the child() method. It's a matter of personal preference.
Get the UID and define a data object:
let user = firebase.auth().currentUser
let uid = user.uid
let yourdata = { foo: 'something', bar: 'other'}
Save the data:
firebase.database().ref('users').child(uid).set(yourdata)
.then((data) => {
console.log('Saved Data', data)
})
.catch((error) => {
console.log('Storing Error', error)
})
Fetch the data:
firebase.database().ref('users').child(uid).once('value')
.then((data) => {
let fetchedData = data.val()
console.log('Fetched Data', fetchedData)
})
.catch((error) => {
console.log('Fetching Error', error)
})
Please notice that set() will override your data so you might want to use push() later on.
Related
I've written a function to execute hourly which looks up a user and finds some values and then pushes those values into a history collection that records the hourly updated values. I've written this so far as a test just finding a user by their ID but now I need to roll this out to my entire database of 50,000+ users.
From what I've read using updateMany is a lot more performant but I'm not entirely sure how to retrieve the document detail of the record that is being updated at the time.
Here is my code so far, which you can see I'm first looking up the user and then grabbing their valuation details which I'd like to then push into a history collection.
exports.updateUserValuationHistoric = () => {
User.find({ _id: "609961fdd989613914ef7216" })
.populate('UserValuationHistory')
.exec((err, userDoc) => {
if (err){
console.log('[ERROR]: ', err)
}
const updatedValuationHistory = {
totalValuation: userDoc[0].valuation.totalValuation,
comicsValuation: userDoc[0].valuation.comicsValuation,
collectiblesValuation: userDoc[0].valuation.collectiblesValuation,
omiValuation: userDoc[0].valuation.omiValuation
}
UserValuationHistory.findOneAndUpdate(
{ user: userDoc[0]._id },
{ $push: {
'history': updatedValuationHistory
}},
{upsert: true, new: true}
)
.exec((error, updated) => {
if (error){
console.log('[ERROR]: Unable to update the user valuation history.')
} else {
console.log('[SUCCESS]: User historic valuation has been updated.')
}
})
})
}
Any help is greatly appreciated!
User model:
https://pastebin.com/7MWBVHf3
Historic model:
https://pastebin.com/nkTGztJY
I'm trying to read and write from/to an Azure Cosmos DB with two different bots (js, v4, ms botframework).
Chatbot 1:
- Chat with user, save user data and use it later
Chatbot 2:
- Read and display some user data
I use the following client: https://github.com/Microsoft/BotFramework-WebChat
Scenario:
I fixate my userID in the client (which has a directline to bot 1) to let's say "123"
I use Bot 1 and enter my username in the dialog (prompted by bot)
I refresh the Website on which Bot 1 is running with the same id "123"
I see that the bot still has my data stored
I change the ID in my client to "124"
I use Bot 1 and see there is no stored data (which is expected since ID "124" has never chatted with Bot 1)
I change the ID back to "123"
I use bot 1 and see that data from step 2 is still there
I use bot 2 with the id "123"
I see that there is no data ("undefined")
I use bot with ID "123" again
I see that the data from step 2 is gone
Which means that whenever I access the database with my second bot it seems like the data is cleared / deleted.
This is how I access the DB in index.js:
//Add CosmosDB (info in .env file)
const memoryStorage = new CosmosDbStorage({
serviceEndpoint: process.env.ACTUAL_SERVICE_ENDPOINT,
authKey: process.env.ACTUAL_AUTH_KEY,
databaseId: process.env.DATABASE,
collectionId: process.env.COLLECTION
})
// ConversationState and UserState
const conversationState = new ConversationState(memoryStorage);
const userState = new UserState(memoryStorage);
// Use middleware to write/read from DB
adapter.use(new AutoSaveStateMiddleware(conversationState));
adapter.use(new AutoSaveStateMiddleware(userState));
This is how I use the DB in bot.js:
constructor(conversationState, userState, dialogSet, memoryStorage) {
// Creates a new state accessor property.
// See https://aka.ms/about-bot-state-accessors to learn more about the bot state and state accessors
this.conversationState = conversationState;
this.userState = userState;
// Memory storage
this.memoryStorage = memoryStorage;
// Conversation Data Property for ConversationState
this.conversationData = conversationState.createProperty(CONVERSATION_DATA_PROPERTY);
// Properties for UserState
this.userData = userState.createProperty(USER_DATA_PROPERTY);
this.investmentData = userState.createProperty(INVESTMENT_DATA_PROPERTY);
}
async displayPayout (step) {
console.log("Display Payout");
// Retrieve user object from UserState storage
const userInvestData = await this.investmentData.get(step.context, {});
const user = await this.userData.get(step.context, {});
await step.context.sendActivity(`Hallo ${user.name}. Am Ausgang kannst du dir deine Bezahlung von ${userInvestData.payout} abholen.` );
}
The code snipped is from bot 2. Bot 1 saves the data in the same way. You can find the repos here:
Bot 1: https://github.com/FRANZKAFKA13/roboadvisoryBot
Bot 2: https://github.com/FRANZKAFKA13/displayBot
Client for Bot 1: https://github.com/FRANZKAFKA13/ra-bot-website-c
Client for Bot 2: https://github.com/FRANZKAFKA13/ra-bot-website-display
I also tried to use the "readOnly" key from CosmosDB in bot 2, which throws an error:
[onTurnError]: [object Object]
(node:1640) UnhandledPromiseRejectionWarning: TypeError: Cannot perform 'set' on a proxy that has been revoked
at adapter.sendActivities.then (C:\Users\X\Implementierung\display_bot\node_modules\botbuilder-core\lib\turnContext.js:175:36)
at <anonymous>
at process._tickDomainCallback (internal/process/next_tick.js:229:7)
(node:1640) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 3)
Another behavior that I have noticed: When I trigger a "join event" through my client with a redux store, the userdata is not saved as well (every time I refresh the page, the data is gone, despite using the same id "123" all the time)
dispatch({
type: 'WEB_CHAT/SEND_EVENT',
payload: {
// Event starting bot's conversation
name: 'webchat/join',
value: {}
}
Any ideas? Thanks in advance
Since storage ids (see image) are created automatically based off of user ids (that may also be created automatically and varies by channel) and channel ids, this can be very difficult to do. It can make it very difficult to persist user and conversation data, particularly across bots and channels.
Example ID:
Here's more on how IDs work.
Personally, I would write my own, custom storage, instead of (or in addition to) saving it with UserState.
To write your data, do something like this:
const changes = {};
const userDataToWrite = {
name: step.result,
eTag: '*',
}
// Replace 'UserId' with however you want to set the UserId
changes['UserId'] = userDataToWrite;
this.memoryStorage.write(changes);
This will store a document that looks like this (I set 'UserId' to 'user123':
To read:
const userDataFromStorage = await this.memoryStorage.read(['UserId']);
userDataFromStorage will look like this:
{ UserId:
{ name: 'myName',
eTag: '"0000c700-0000-0000-0000-5c7879d30000"' } }
You'll have to manage userIds yourself, but this will ensure that the data can be read across bots, channels, and users.
Edit: Solved it by adding "[this.userID]" after each "user" call.
I tried your method and whenever I write the data, a new eTag is created which leads to the object being split apart:
"document": {
"25781dc4-805d-4e69-bf89-da1f4d72e7cb": {
"25781dc4-805d-4e69-bf89-da1f4d72e7cb": {
"25781dc4-805d-4e69-bf89-da1f4d72e7cb": {
"25781dc4-805d-4e69-bf89-da1f4d72e7cb": {
"25781dc4-805d-4e69-bf89-da1f4d72e7cb": {
"name": "",
"age": "",
"gender": "",
"education": "",
"major": "",
"eTag": "\"00003998-0000-0000-0000-5c797fff0000\""
},
"name": "Jane Doe",
"eTag": "\"00003e98-0000-0000-0000-5c7980080000\""
},
"age": 22,
"eTag": "\"00004898-0000-0000-0000-5c7980150000\""
},
"gender": "female",
"eTag": "\"00004d98-0000-0000-0000-5c79801b0000\""
},
"education": "Bachelor",
"eTag": "\"00005498-0000-0000-0000-5c7980200000\""
},
"major": "Business Administration",
"complete": true
How can I prevent this?
My Code:
In Constructor:
this.changes = {};
this.userID = "";
this.userDatax = {
name: "",
age: "",
gender: "",
education: "",
major: "",
eTag: '*',
}
In Dialogs:
async welcomeUser (step) {
console.log("Welcome User Dialog");
//step.context.sendActivity({ type: ActivityTypes.Typing});
// Initialize UserData Object and save it to DB
this.changes[this.userID] = this.userDatax;
await this.memoryStorage.write(this.changes);
}
async promptForAge (step) {
console.log("Age Prompt");
// Read UserData from DB
var user = await this.memoryStorage.read([this.userID]);
console.log(user);
// Before saving entry, check if it already exists
if(!user.name) {
user.name = step.result;
user.eTag = '*';
// Write userData to DB
this.changes[this.userID] = user;
await this.memoryStorage.write(this.changes);
}
}
I am trying to mock out a user for testing out my application, and I have gotten to the point where I can create a test user and log them into the mirror instance of my app.
I need to compare the gmail addresses for peoples accounts, and to test this functionality, I want to add a test email address under user.services.google.email within the Meteor users account database (which is where the accounts-google package stores it, I don't need to mock out an entire user account yet).
What I can't figure out is how to append this information, instead of just overwriting what is already there, this is what my code looks like:
if (Meteor.users.find().count() === 0) {
var testUserDetails = {
email: 'testEmail#gmail.com',
password: 'testPassword'
};
console.log("Creating the Test User");
var newUserId = Accounts.createUser(testUserDetails);
Meteor.users.update({
_id: newUserId
}, {
$set: {
services: {
google: {
email: "testEmail#gmail.com"
}
}
}
});
} else {
console.log("There are already users in the Test database");
}
console.log('***** Finished loading default fixtures *****');
},
And this is what a user looks like:
{
"_id" : "Dw2xQPDwKp58RozC4",
"createdAt" : ISODate("2015-07-30T04:02:03.261Z"),
"services" : {
"password" : {
"bcrypt" : "asdfasdfasdfdsafsadfasdsdsawf"
},
"resume" : {
"loginTokens" : [ ]
}
},
"emails" : [
{
"address" : "testEmail#gmail.com",
"verified" : false
}
]
}
Now $set just rewrites everything within services, and there is no $push operation for mongo or for js, so how should I go about doing this? Should I consume the object and parse it manually?
*Note I have also tried using Meteor's Accounts.onCreateUser(function(options, user) but facing the same issue.
[...] there is no $push operation for mongo [...]
Sure, there is a $push operator, which appends a specified value to an array.
However, I think what you are trying to do is to update a document and keep all values which are already set.
Here is how you can do that:
Query the document first to get the object you want to set.
Update the respective object.
Run the MongoDB update operation to set the new object.
For instance:
var user = Meteor.users.findOne({
_id: newUserId
});
var servicesUserData = user.services;
servicesUserData.google.email = "your_new_email#gmail.com";
Meteor.users.update({
_id: newUserId
}, {
$set: {
"services": {
servicesUserData
}
}
});
I'm building a multiplayer, turn-based game using meteor.js. The application will handle multiple games, so I'd like to separate my users into rooms.
I've done it before using socket.io channels, but I'm struggling to understand how it should be done in Meteor.
The flow I'd like to achieve is:
User visits http://localhost:3000/join/userId
I make a server-side call to an external API using "sessionId" as parameter, getting user's userId, his assigned roomId and an array of allowed userId's for this room
I'd like to create a room with roomId for the user or join him to an existing one. I know I should create a 'Rooms' collection, but I don't know how to tie users to my rooms and publish messages only to those present in the given room.
I'd like to avoid using 'accounts' package, because I don't need authorisation on my side - it'll be handled by step #2 mentioned above - but if the easiest and cleanest way of doing it involves adding this package, I can change my mind.
Your Rooms collection could look like:
{
_id: "<auto-generated>",
roomId: "roomId",
users: [ "user1", "user2", "user3", ... ],
messages: [
{ message: "", userId: "" },
{ message: "", userId: "" },
{ message: "", userId: "" },
...
]
}
The server-side API call returns
userId and roomId among other information.
So you can do a
Rooms.update({ roomId: roomId }, { $push: { users: userId } }, { upsert: true });
This would push the user into the exiting room or create a new room and add the user.
Your publish function could look like:
Meteor.publish("room", function(roomId) {
// Since you are not using accounts package, you will have to get the userId using the sessionId that you've specified or some other way.
// Let us assume your function getUserId does just that.
userId: getUserId( sessionId );
return Rooms.find({ roomId: roomId, users: userId });
// Only the room's users will get the data now.
});
Hope this helps.
I'm working on a simple login system for my NodeJS application. For this I have created a structure where one object, a "corporation", holds an array of users. I've done because I plan to use the corporation object to store application session data.
{
"name": "My Corporation",
"prefix": "MYCORP",
"users": [
{
"username": "some#user.com",
"password": "974dae09cd5869958c19e1742117c2f8",
"name": "Freddly the User"
},
{
"username": "other#user.com",
"password": "974dae09cd5869958c19e1742117c2f8",
"name": "Max the Admin"
}
]
}
The problem is when querying after a user (in a log-in scenario) the query, as expected, returns the entire corporation object. Thus I'm exposing all users even though I only want one. As far as security is concerned I guess it isn't a big deal, but I'm more worried about performance. Below is the current query and a very ugly way to delete all users but the one requested.
Ignore the different asserts. Code is very much work-in-progress .. :)
db.collection('kat_corp', function (err, collection) {
try {
assert.equal(null, err);
collection.findOne({
users: {
$elemMatch: {
username: user.username
}
}
}, function (err, result) {
if (err) callback(err, false);
// Delete all other users from the to-be session object
for (var i = 0; i < result.users.length; i++) {
if (result.users[i].username != user.username) {
delete result.users[i];
}
}
// Will be replaced with success callback
console.log(result);
});
} catch (err) {
callback(err, false);
}
});
If you're using MongoDB 2.2 or greater you can just use the "$" positional operator.
The following query worked for me :
db.collection('kat_corp', function (err, collection){
collection.findOne({"users.username":user.username}, {'name':1,'users.$': 1}, console.log)
});
Although I would agree with the other comments that you should probably reconsider your schema...