I'm using meteor tabular and publish composite addons.
I want to pub/sub user data based on user role, user data is getting send to client however not being displayed by tabular addon (for admin role only, it display fine for super-admin role, see code below).
My publish code:
Meteor.publishComposite('tabular_users', function (tableName, ids, fields) {
this.unblock();
return {
find: function () {
if (Roles.userIsInRole(this.userId, ['super-admin'], 'admin')) {
return Meteor.users.find({_id: {$in: ids}}, {fields: fields});
} else if (Roles.userIsInRole(this.userId, ['admin'], 'property-managers')) {
return Meteor.users.find({ "$and" : [
{ _id: { $in: ids } },
{ "profile.property_manager_id": Meteor.users.findOne(this.userId).profile.property_manager_id }
]}, {fields: fields});
} else {
this.stop();
return;
}
},
children: [
{
find: function(user) {
return PropertyManagers.find(
{ _id: user.profile.property_manager_id }
);
}
}
]
};
});
Related
I'm using GraphQL with Meteor and Pup v2, and I have a problem accessing the users data via a special ID provided to every user on signup, this ID will be used in a link (mysite.com/user/specialId) so other users can view the searched users account. Problem is, I can't get the data with the special ID, I can't get any data back if I don't pass in the users _id provided by MongoDB. Below I have a bunch of the code used:
Attempt 1
I tried to use a custom on-the-go way just to be able to at least access the data to see if it works (and then implement it correctly later)
const GET_USER_DETAILS = gql`
query user($userId: String) {
user(userId: $userId) {
userId
username
_id
}
}
`;
Here I export so I can get the data:
export default compose(
graphql(GET_USER_DETAILS, {
options: ({ match }) => ({
fetchPolicy: 'no-cache',
variables: {
// existing specialId for testing purposes, to be replaced with match.params.userId
userId: "J4xZzvvhBDSEufnBn",
},
}),
}),
)(PublicProfileView);
This returns a 400 error Network error: Response not successful: Received status code 400 error and after multiple attempts, I could not fix it, so I tried a different approach...
Attempt 2
I tried to go deep into the files and change the GraphQL. Created a new query:
query userById($userId: String) {
userById(userId: $userId) {
...UserAttributes
}
}
(Mentioned fragment)
fragment UserAttributes on User {
_id
name {
...
}
username
emailAddress
oAuthProvider
roles {
...
}
settings {
...
}
userId
}
Tried to add new item in API:
type Query {
...
userById(userId: String): User
...
}
Resolver:
resolvers: {
Query: {
...
userById: (parent, args) => {
// Assuming args equals an object like { _id: '123' };
return UserQueries.userById(args);
},
},
},
query.js, attempt 1:
userById: (parent) => queryUsers.find({ userId: parent.userId }, { sort: { createdAt: 1 } }).fetch()
Attempt 2:
userById: (parent, args, context) => {
return queryUsers({
userId: parent.userId,
});
},
And finally
Attempt 3
I tried to modify the get query
const getQueryModified = (options) => {
// console.log(options.userId)
try {
return options.userId
? { 'userId': options.userId }
: { userId: options.userId };
} catch (exception) {
throw new Error(`[queryUsers.getQuery] ${exception.message}`);
}
};
Here is the original query I tried to modify:
const getQuery = (options) => {
try {
return options.search
? {
_id: { $ne: options.currentUser._id },
$or: [
{ 'profile.name.first': options.search },
{ 'profile.name.last': options.search },
{ 'emails.address': options.search },
// { 'userId': options.search },
{ 'services.facebook.first_name': options.search },
{ 'services.facebook.last_name': options.search },
{ 'services.facebook.email': options.search },
],
}
: { _id: options.currentUser._id };
} catch (exception) {
throw new Error(`[queryUsers.getQuery] ${exception.message}`);
}
};
Unfortunately this was also unsuccessful, the best I get from these when executing the below query is null...
userById(userId: "J4xZzvvhBDSEufnBn"){
username
}
All I want is to get the user data from their userId and not their _id, but I can't seem to figure out how to do it
I'm having a problem with a publish function. If I run the code without the roles package it works, however, when I add the roles if statement it doesn't. What am I missing here?
Path: publish.js (working)
Meteor.publish('userProfile', function(id) {
check(id, String);
return Meteor.users.find({_id: id}, {
fields: {
"profile.firstName": 1,
"profile.familyName": 1
}
});
});
Path: publish.js (not working)
Meteor.publish('userProfile', function(group, id) {
if (Roles.userIsInRole(this.userId, ['is_admin'], group)) {
check(id, String);
return Meteor.users.find({_id: id}, {
fields: {
"profile.firstName": 1,
"profile.familyName": 1
}
});
} else {
// user not authorized. do not publish secrets
this.stop();
return;
}
});
I am building a Chat app in Meteor 1.3, ES6 and React.
I have 3 collections:
1. People: Who has an array of conversations that the person are involved.
2. Conversations: That also have the people that are involved.
3. Messages: That has a conversation_id field to relate to second collection.
So I am using reywood:publish-composite package, to manage the reactive joins:
=> Get Conversations for this People (user)
=> Get the Messages of all the Conversations of this user
=> Filter messages according to the conversation selected (a react state)
I am also using the package React-Komposer from Kadira.
This is my code:
// publications.js
Meteor.publishComposite('compositeChat', function (people_id) {
return {
find() {
return Collections.People.find({_id: people_id});
},
children: [
{
find(people) {
return Collections.Conversations.find(
{_id: {$in: people.conversations }},
{sort: {'lastMessage.date': -1}}
);
}
},
{
find(people) {
return Collections.Messages.find(
{conversation_id: {$in: people.conversations }},
{sort: {date: 1}}
);
}
},
{
find(people) {
return Collections.People.find(
{_id: {$in: people.contacts }}
);
}
}
]
};
});
// AppContainer.js
import {composeAll, composeWithTracker} from 'react-komposer';
import AppWrapper from '../AppWrapper.jsx';
import * as Collections from '../../../../lib/collections/collections.js';
function composeChat(props, onData) {
let user;
if (!Meteor.userId()) {
user = 'qXSWv64qKaEPAu5yp';
} else {
user = Meteor.userId();
//console.log('Meteor.userId()', Meteor.userId());
}
const
chatSub = Meteor.subscribe('compositeChat', user);
if (chatSub.ready()) {
const chatData = {
chatLoading:
!chatSub.ready(),
myUserData:
Collections.People.findOne({_id: user}),
myContacts:
Collections.People.find(
{_id: { $ne: user}}, { sort: { name: 1 } }
).fetch(),
myConversations:
Collections.Conversations.find(
{}, { sort: { date: -1 } }
).fetch(),
noConversationContacts:
(function () {
return myFunctions.chat.noConversationContacts(
Collections.People.find(
{_id: { $ne: user}}
).fetch(),
Collections.Conversations.find().fetch()
);
}()),
myMessages:
Collections.Messages.find(
{}, {sort: {'lastMessage.date': -1}}
).fetch(),
myOtherPeople:
(function () {
return myFunctions.chat.getTheOtherPeople(
Collections.Conversations.find().fetch(),
user
);
}()),
unreaded:
(function () {
return myFunctions.chat.countUnreadedMessages(
user,
Collections.Conversations.find().fetch(),
Collections.Messages.find().fetch()
);
}())
};
console.log('chatData', chatData);
onData(null, {chatData});
}
}
export default composeWithTracker(composeChat)(AppWrapper);
The problem is: When new message is added, I am not getting the update in the UI, but the data is there (in Mongo), so if I refresh the page, I get the new messages...
In my previous version of the app (Meteor 1.2) this reactive joins worked perfectly.
What could be wrong?
Does publish-composite package not work with Meteor 1.3?
Does publish-composite can't be used with React-Komposer?
Do I have to mix them in other way?
Is there another option (with or without this packages) to manage reactive joins in Meteor 1.3?
Thanks
Meteor.publishComposite('jobs', {
find: function() {
var user = null;
if (this.userId) {
user = Meteor.users.findOne(this.userId);
if ( user && user.profile && user.profile.isAdmin ) {
return Jobs.find({}, { sort: { createdAt: -1 }});
} else if(user && user._id) {
return Jobs.find({'createdBy': user._id});
}
} else {
return this.ready();
}
},
children: [
{
find: function(job) {
// Find post author. Even though we only want to return
// one record here, we use "find" instead of "findOne"
// since this function should return a cursor.
return Meteor.users.find(
{
_id: job.createdBy
},
{
fields: {
'profile': 1,
'createdAt': 1
}
}
);
}
}
]
});
This is the code I'm using from meteor-publishComposite package. I do not get the profile on my subscriptions for some reason. I can get the user.services to show up, but not the user.profile.
So I have a route that sets my template
Router.route('audit', {
path: '/audit/:audit_id/',
template: 'audit',
data: function() {
if (this.ready()) {
audit_obj = Audits.findOne({_id: this.params.audit_id});
lineitems = LineItems.find(JSON.parse(audit.query));
return {
audit_obj: audit_obj,
lineitems: lineitems
}
}
},
waitOn: function () {
return [
Meteor.subscribe('lineitems', this.params.audit_id),
Meteor.subscribe('audits')
]
}
}
Now, when my user takes certain actions on the page rendered by the audit template, I would like to update the audit object and also update the data context that the page is running with. Is this possible?
Something like:
Template.audit.events({
'click .something-button': function() {
// update the data context for the current audit template.
current_context.audit_obj.something = 'new something';
}
});
Yes:
Router.route('audit', {
path: '/audit/:audit_id/',
template: 'audit',
onRun: function() {
Session.set('audit', Audits.findOne(this.params.audit_id));
Session.set('lineitems', LineItems.find(JSON.parse(audit.query)).fetch());
}
data: function() {
if (this.ready()) {
return {
audit_obj: Session.get('audit'),
lineitems: Session.get('lineitems')
}
}
},
waitOn: function () {
return [
Meteor.subscribe('lineitems', this.params.audit_id),
Meteor.subscribe('audits')
]
}
}
and
Template.audit.events({
'click .something-button': function() {
// update the data context for the current audit template.
Session.set('audit', {..});
}
});
But you'll need to decide how to handle changes that come from the server, and may interfere with changes on the front end. So a better approach might be to leave the first part of the code (router) as is:
Router.route('audit', {
path: '/audit/:audit_id/',
template: 'audit',
data: function() {
if (this.ready()) {
return {
audit_obj: Audits.findOne(this.params.audit_id),
lineitems: LineItems.find(JSON.parse(audit.query))
}
}
},
waitOn: function () {
return [
Meteor.subscribe('lineitems', this.params.audit_id),
Meteor.subscribe('audits')
]
}
}
and just change the front end to update the collection:
Template.audit.events({
'click .something-button': function() {
// update the data context for the current audit template.
Audits.update( this.data.audit_obj._id, {..} );
}
});
Of course, that will update the data on the server, too.