Accounts-meld is not working for me, should it work out the box or do I need to do something? I am rolling my own ui and using Meteor.loginWithXX() for Facebook and Twitter. I have tried creating an account manually then logging in with a third party however it just creates a new user and doesn't merge them.
Am I doing something wrong?
I am configuring my services with
Accounts.loginServiceConfiguration.insert({
"service": "facebook",
"appId": "XXXXXXXXXXXX",
"secret": "XXXXXXXXXXXX"
});
Accounts.loginServiceConfiguration.insert({
"service": "twitter",
"consumerKey": "XXXXXXXXXXXX",
"secret": "XXXXXXXXXXXX"
});
Then I use Meteor.loginWithFacebook(); and Meteor.loginWithTwitter();
Any help would be greatly appreciated
Let me post the code I'm using and you can let me know if it's helpful. It's not my code and was gleamed from other answers but from so many I can't provide sources.
Your right to use service configuration to set up each service but you do need to install the package for that if you haven't already.
I then added an event that looks something like this for each login service I offer.
Template.login.events({
"click #loginWithFacebook": function (event) {
event.preventDefault();
Meteor.loginWithFacebook({
}, function(error) {
if (error) {
console.log(error);
}
});
}
});
I also then have an onCreateUser code block which does a check to see if it's a new user or if they are just using a new service as their login provider. This has been tweaked a little so you will need to take out the stuff that's not relevant.
Accounts.onCreateUser(function(options, user) {
var email, oldUser, service;
if (user.profile == null) {
user.profile = {};
if (options.profile != null) {
user.profile.name = options.profile.name;
user.profile.organisation = options.profile.organisation;
}
}
if (user.services != null) {
service = _.keys(user.services)[0];
email = user.services[service].email;
if (email != null) {
oldUser = Meteor.users.findOne({
"emails.address": email
});
if (oldUser != null) {
if (oldUser.services == null) {
oldUser.services = {};
}
if (service === "google" || service === "facebook") {
oldUser.services[service] = user.services[service];
Meteor.users.remove(oldUser._id);
user = oldUser;
}
} else {
if (service === "google" || service === "facebook") {
if (user.services[service].email != null) {
user.emails = [
{
address: user.services[service].email,
verified: true
}
];
} else {
throw new Meteor.Error(500, "" + service + " account has no email attached");
}
user.profile.name = user.services[service].name;
user.profile.organisation = Organisations.find({}, {'fields': {'_id':1}}).fetch()[0];
}
}
}
}
return user;
});
Related
I'm building an app where users can exchange locations.
So far I've built a function that the user can use to send his location and any other user can see it. I'd like to modify my sendLocation() function so it sends the location to a specific user, and only that user would be able to see it, but I'm not sure what's the best way to do this.
This send the user location to the database
sendLocation = () => {
console.warn("sending location log", this.props);
firebase
.database()
.ref("/locations")
.child(this.currentUser.uid)
.child(Date.now())
.set({
uid: this.currentUser.uid,
user: user,
latitude: this.props.location.coords.latitude,
longitude: this.props.location.coords.longitude,
created_at: Date.now(),
order: -Date.now()
});
this.sendPushNotification();
};
Here is where I retrieve the locations
readLocations = () => {
allLocations = [];
let locations = firebase
.database()
.ref("/locations")
.child(this.currentUser.uid)
.orderByChild("created_at")
.startAt(last12hours);
locations.on("value", snapshot => {
snapshot.forEach(thing => {
oneLocation = [];
oneLocation.push(
thing.val().uid,
thing.val().latitude,
thing.val().longitude,
thing.val().user
);
allLocations.push(oneLocation);
});
this.setState({ locations: allLocations }, () => {
});
});
};
I also have access to the users information. This function would be triggered BEFORE I send the location to the user.
readFriends = () => {
allFriends = [];
let myFriends = firebase
.database()
.ref("/users")
.orderByChild("first_name");
myFriends.on("value", snapshot => {
snapshot.forEach(thing => {
oneFriend = [];
oneFriend.push(thing.val().first_name, thing.val().last_name);
allFriends.push(oneFriend);
});
this.setState({ friends: allFriends, modalVisible: true }, () => {
});
});
};
To be able to detect whether somebody is writing their location to a friend, you'll first need to model friendships in the database. A very simple model for that is:
friends: {
uid1: {
uid2: true,
uid3: true
},
uid2: {
uid1: true
}
}
So in the above data structure, uid1 has marked uid2 and uid3 as their friends, while uid2 has reciprocated and marked uid1 as their friend. You'd typically secure the above so that a user can only write their own friends with:
{
"rules": {
"friends": {
"$uid": {
".write": "auth.uid === $uid"
}
}
}
}
Now with the above we can allow a user to write their location for people that have marked them as friends. We'll use another data structure for that, something like:
locations: {
uid1: {
uid2: "location of uid2"
}
}
So in the above scenario, user uid2 has written their location to uid1/uid2 with something like:
firebase.database()
.ref("/locations")
.child("uid1") // the UID of someone who marked us as a friend
.child(this.currentUser.uid)
.set(...)
And you'd secure the above write operation with:
{
"rules": {
"locations": {
"$friendid": {
"$uid": {
".write": "auth.uid === $uid &&
root.child('friends').child($friendid).child(auth.uid).exists()"
}
}
}
}
}
I am currently on a project and i need to mock a certain constructor class prototype to run test so it doesn't deal directly with my database. I already tried researching and discovered i need to import the module into my mock file and then export it to my test file and use jest.setMock() to replace the module in my test.js file. Below is the code to what i have tried.
I have an Admin constructor class with a delete function in its prototype responsible for deleting from db
admin.js
function Admin(username, email, password, access) {
Users.call(this, username, email, password, access);
}
Admin.constructor = Admin;
Admin.prototype.deleteSingleUser = function(id, accountType) {
this.id = id;
this.accountType = accountType;
let response = "";
if(this.accountType === "admin") {
for(let i in dbData.admin) {
if(dbData.admin[i].id === this.id) {
dbData.admin.splice(i, 1);
fs.writeFileSync('db.json', JSON.stringify(dbData, null, 2));
console.log(dbData.admin);
response = "Account successfully deleted";
}
else {
response = "There is no user registered with this ID";
}
}
}
else if(this.accountType === "user") {
for(let i in dbData.users) {
if(dbData.users[i].id === this.id) {
dbData.users.splice(i, 1);
fs.writeFileSync('db.json', JSON.stringify(dbData, null, 2));
console.log(dbData.users);
response = "Account successfully deleted";
}
else {
response = "There is no user registered with this ID";
}
}
}
else {
response = "Kindly use a valid account type";
}
console.log(response);
return response;
};
Then in my __mocks__ directory i have an admin.js file containing a mock database and my mock delete code
__mocks__/admin.js
//Imported the admin.js
let Admin = require("../admin");
//Created a mock database
let mockDb = {
"users": [
{
"id": 1,
"username": "aaa",
"email": "aaa#gmail.com",
"password": "aaa",
"access": "user"
}
],
"admin": [
{
"id": 1,
"username": "bbb",
"email": "bbb#gmail.com",
"password": "bbb",
"access": "admin"
}
]
}
//My mock delete function
Admin.prototype.deleteSingleUser = function(id, accountType) {
this.id = id;
this.accountType = accountType;
let response = "";
if (this.accountType === "admin") {
if(mockDb.admin[0].id === this.id) {
response = "Account successfully deleted";
}
else {
response = "There is no user registered with this ID";
}
}
else if (this.accountType === "user") {
if(mockDb.users[0].id === this.id) {
response = "Account successfully deleted";
}
else {
response = "There is no user registered with this ID";
}
}
console.log("Its using the mock");
return response;
};
module.exports = Admin;
Then in my __test__ folder i have a test.js file with my test cases
__test__/test.js
const Admin = require("../admin");
jest.setMock("Admin", require("../__mocks__/admin"));
test("Should check if admin can delete with a wrong id", () => {
let response = Admin.prototype.deleteSingleUser(0, "user");
expect(response).toBe("There is no user registered with this ID");
});
test("Should check if admin can delete a user", () => {
let response = Admin.prototype.deleteSingleUser(1, "user");
expect(response).toBe("Account successfully deleted");
});
Neither of the test cases pass but both throw an error saying
Test suite failed to run
Cannot find module 'Admin' from 'test.js'
2 | const Users = require("../main");
3 | const Admin = require("../admin");
4 | jest.setMock("Admin", require("../mocks/admin"));
| ^
5 | const order = require("../order");
6 | let mocks = require("../mocks/admin");
7 | let dbData = JSON.parse(fs.readFileSync('db.json'));
at Resolver.resolveModule (node_modules/jest-resolve/build/index.js:229:17)
at Object.setMock (test/test.js:4:6)
I feel i am not doing something right but i can't seem to point it out. Please what is the issue and how do i fix it? Thanks.
You don't need to use jest.setMock at all.
You just need to activate the manual mock you created for your user module by doing this:
jest.mock('../admin'); // <= activate the manual mock
Details
The docs for jest.setMock state that it should only be used on "extremely rare occasions" when "even a manual mock isn't suitable for your purposes" and adds that it is "recommended to use jest.mock() instead."
In this case you aren't doing anything that would require jest.setMock so best practice is to activate your manual mock with jest.mock.
Your code seems alright. You just need to set your jest.setMock() to point to the right file.
jest.setMock() takes two parameters jest.setMock(moduleName, moduleExports). The moduleName should point to the file containing the module your are mocking and moduleExports to the file containing your mock module. In other words, change in your
__test__/test.js
jest.setMock("Admin", require("../__mocks__/admin"));
to
jest.setMock("../admin", require("../__mocks__/admin"));
And you should be fine.
I am currently working on an app using firebase and angularJS (ionic). Basically this is a car management app, so you have people sharing their cars with others. I tried to structure the data as flat as possible to be efficient. My issue here is that if without problem I can display the list of the car_id of the different cars shared with the logged user, I can't find a way to display the list of cars shared with the user displaying the year and the model.
Thank you in advance for your help !
{
"rules": {
"users": {
".write": true,
"$uid": {
".read": "auth != null && auth.uid == $uid"
},
"cars": {
"car_id":true,
"role":true // Owner, borower...
}
},
"cars": {
"car_id":true,
"model":true,
"year":true
}
}
}
carapp.controller("carsController", function($scope, $firebaseObject, $ionicPopup, $ionicHistory) {
$ionicHistory.clearHistory();
$scope.list = function() {
frbAuth = frb.getAuth();
if(frbAuth) {
var userObject = $firebaseObject(frb.child("users/" + frbAuth.uid));
userObject.$bindTo($scope, "user");
$scope.cars = frb.child("cars");
}}
$scope.createCar = function() {
$ionicPopup.prompt({
model: 'Create a new car',
inputType: 'text'
})
.then(function(result) {
if(result !== "") {
var newCar = $scope.cars.push({
model: result
})
var newCarId = newCar.key();
$scope.user.cars.push({car_id: newCarId, role: "owner" });
} else {
console.log("Action not completed");
}
});
}
});
<div class="list">
<a ng-repeat="car in user.cars" >
<h2>{{car.car_id}}</h2> ----> works fine !
<h2>{{car.model}}</h2> ----> How to get this working ?
<h2>{{car.year}}</h2> ----> How to get this working ?
</a>
</div>
In the users/ path, begin by storing the list of cars by index, instead of in an array. So your structure would be:
{
"users": {
"kato": {
"cars": {
"DeLorean": true
}
}
},
"cars": {
"DeLorean": {
model: "DeLorean",
year: "1975"
}
}
}
To join this using AngularFire, you have several approaches available. An AngularFire-only solution might look like this, taking advantage of $extend:
app.factory('CarsByUser', function($firebaseArray) {
return $firebaseArray.$extend({
$$added: function(snap) {
return new Car(snap);
},
$$updated: function(snap) {
// nothing to do here; the value of the index is not used
},
$$removed: function(snap) {
this.$getRecord(snap.key()).destroy();
},
// these could be implemented in a manner consistent with the
// use case and above code, for simplicity, they are disabled here
$add: readOnly,
$save: readOnly
});
var carsRef = new Firebase(...).child('cars');
function Car(snap) {
// create a reference to the data for a specific car
this.$id = snap.key();
this.ref = carsRef.child(this.$id);
// listen for changes to the data
this.ref.on('value', this.updated, this);
}
Car.prototype.updated = function(snap) {
this.model = data.model;
this.year = data.year;
}
Car.prototype.destroy = function() {
this.ref.off('value', this.meta, this);
};
function readOnly() { throw new Error('This is a read only list'); }
});
app.controller('...', function($scope, CarsByUser, authData) {
// authenticate first, preferably with resolve
var ref = new Firebase(...).child(authData.uid);
$scope.cars = CarsByUser($scope);
});
For a more sophisticated and elegant approach, one could utilize NormalizedCollection and pass that ref into the AngularFire array:
app.controller('...', function($scope, $firebaseArray) {
var ref = new Firebase(...);
var nc = new Firebase.util.NormalizedCollection(
ref.child('users/' + authData.uid),
ref.child('cars')
)
.select('cars.model', 'cars.year')
.ref();
$scope.cars = $firebaseArray(nc);
});
I am currently working on an app using firebase and angularJS (ionic). Basically this is a car management app, so you have people sharing their cars with others. I tried to structure the data as flat as possible to be efficient. My issue here is that if without problem I can display the list of the car_id of the different cars shared with the logged user, I can't find a way to display the list of cars shared with the user displaying the year and the model.
Thank you in advance for your help !
{
"rules": {
"users": {
".write": true,
"$uid": {
".read": "auth != null && auth.uid == $uid"
},
"cars": {
"car_id":true,
"role":true // Owner, borower...
}
},
"cars": {
"car_id":true,
"model":true,
"year":true
}
}
}
carapp.controller("carsController", function($scope, $firebaseObject, $ionicPopup, $ionicHistory) {
$ionicHistory.clearHistory();
$scope.list = function() {
frbAuth = frb.getAuth();
if(frbAuth) {
var userObject = $firebaseObject(frb.child("users/" + frbAuth.uid));
userObject.$bindTo($scope, "user");
$scope.cars = frb.child("cars");
}}
$scope.createCar = function() {
$ionicPopup.prompt({
model: 'Create a new car',
inputType: 'text'
})
.then(function(result) {
if(result !== "") {
var newCar = $scope.cars.push({
model: result
})
var newCarId = newCar.key();
$scope.user.cars.push({car_id: newCarId, role: "owner" });
} else {
console.log("Action not completed");
}
});
}
});
<div class="list">
<a ng-repeat="car in user.cars" >
<h2>{{car.car_id}}</h2> ----> works fine !
<h2>{{car.model}}</h2> ----> How to get this working ?
<h2>{{car.year}}</h2> ----> How to get this working ?
</a>
</div>
In the users/ path, begin by storing the list of cars by index, instead of in an array. So your structure would be:
{
"users": {
"kato": {
"cars": {
"DeLorean": true
}
}
},
"cars": {
"DeLorean": {
model: "DeLorean",
year: "1975"
}
}
}
To join this using AngularFire, you have several approaches available. An AngularFire-only solution might look like this, taking advantage of $extend:
app.factory('CarsByUser', function($firebaseArray) {
return $firebaseArray.$extend({
$$added: function(snap) {
return new Car(snap);
},
$$updated: function(snap) {
// nothing to do here; the value of the index is not used
},
$$removed: function(snap) {
this.$getRecord(snap.key()).destroy();
},
// these could be implemented in a manner consistent with the
// use case and above code, for simplicity, they are disabled here
$add: readOnly,
$save: readOnly
});
var carsRef = new Firebase(...).child('cars');
function Car(snap) {
// create a reference to the data for a specific car
this.$id = snap.key();
this.ref = carsRef.child(this.$id);
// listen for changes to the data
this.ref.on('value', this.updated, this);
}
Car.prototype.updated = function(snap) {
this.model = data.model;
this.year = data.year;
}
Car.prototype.destroy = function() {
this.ref.off('value', this.meta, this);
};
function readOnly() { throw new Error('This is a read only list'); }
});
app.controller('...', function($scope, CarsByUser, authData) {
// authenticate first, preferably with resolve
var ref = new Firebase(...).child(authData.uid);
$scope.cars = CarsByUser($scope);
});
For a more sophisticated and elegant approach, one could utilize NormalizedCollection and pass that ref into the AngularFire array:
app.controller('...', function($scope, $firebaseArray) {
var ref = new Firebase(...);
var nc = new Firebase.util.NormalizedCollection(
ref.child('users/' + authData.uid),
ref.child('cars')
)
.select('cars.model', 'cars.year')
.ref();
$scope.cars = $firebaseArray(nc);
});
I have Firebase entries in /items with the properties title and points. I am trying to check to see if an item of the same title exists before entering a new one.
This is what I have but it does not happen:
app.controller('ItemController', function($scope, FURL, $firebase, $location, toaster) {
var ref = new Firebase(FURL);
var fbItems = $firebase(ref.child('items')).$asArray();
$scope.addItem = function(item) {
// var iItem = $scope.item.title;
var userId = $scope.item.title;
checkIfUserExists(userId);
};
function userExistsCallback(userId, exists) {
if (exists) {
alert('user ' + userId + ' exists!');
} else {
alert('user ' + userId + ' does not exist!');
}
}
function checkIfUserExists(userId) {
ref.child('items').once('value', function(snapshot) {
var exists = (snapshot.val() !== null);
userExistsCallback(userId, exists);
});
}
});
The Realtime Database is a key/value JSON database. This means that if you store a title name as a key, it will be super quick to look it up.
Take the following data for example.
{
"items": {
"title-1": {
"something": "foo"
},
"title-2": {
"something": "baz"
}
}
}
Now let's say we want to check to see if title-2 exists. We can do this with an easy read.
function checkForTitle(title, cb) {
var itemsRef = new Firebase('<my-firebase-app>/items');
var titleRef = itemRef.chld(title).once('value', function(snap) {
cb(snap.exists());
});
}
checkForTitle('title-2', function(doesExist) {
console.log(doesExist); // true
});
To make sure the check happens on the server, you can write a Security Rule for it. Or, better yet use the new Bolt Compiler.
{
"items": {
"$item": {
".write": "!data.exists()" // don't overwrite existing data
}
}
}
You should upgrade your AngularFire version. I noticed you're using $firebase().$asArray which means you're on the 0.9 version of AngularFire, which is unsupported. Look into upgrading to the 1.0+ version which is officially support by Firebase.