This is what I was referring to:
Is there some way we can make the text clearer?
Ohhhhhhhhhh wait I got totally confused. You were referring to the code snippet above from Todos. I was thinking of this section: http://guide.meteor.com/security.html#allow-deny
My mistake.
I have a ValidatedMethod that updates a collection document. Only the document author should be able to make updates. I tried to implement the editableBy helper from the todos example using dburles:collection-helpers as recommend in the Guideās Collection helpers section.
const project = Projects.findOne({ _id: projectId });
if (!project.editableBy(this.userId)) {
throw new Meteor.Error('Projects.methods.edit.unauthorized', 'Only author can edit a project.');
}
It kept failing and then I re-read the Errors in method simulation section.
ValidatedMethod turns on undocumented option in Meteor to avoid calling the server-side implementation if the simulation throws an error.
I realized that the findOne in my code above would only work on the server. On the client it would always return null and throw and error which would block the server code. As a fix, I just wrapped the above code with !this.isSimulation
How does the Todos example work, because it doesnāt use isSimulation?
In Todos we publish the fields required to check the permissions to the client. But if you donāt, youāll need to make the check server-only by using isSimulation.
Sorry, Iām having a hard time figuring out where the example makes those fields public. I tried adding publicFields but it had no effect.
Projects.publicFields = {
authorId: 1,
};
Which file actually makes it happen? Does it require and additional library like reywood:publish-composite?
What about Typescript and schema? aldeed:simple-schema
does not seem to provide a definition type. So it could be useful to write just a quote on what to use for schema validation with typescript.
In todos the publicFields
restriction is used in the lists.public
Meteor.publish
declaration. The values set in Lists.publicFields
are used in the Collectionās find query, as Mongo field specifiers.
Thanks but my original question was how does the Todos example make certain fields of a collection accessible to a Meteor methodās client side simulation. Sorry if Iām missing something simple.
Right - letās use the todos lists.updateName
ValidatedMethod as an example:
export const updateName = new ValidatedMethod({
...
run({ listId, newName }) {
const list = Lists.findOne(listId);
if (!list.editableBy(this.userId)) {
throw new Meteor.Error('lists.updateName.accessDenied',
'You don\'t have permission to edit this list.');
}
...
},
});
When run on the client side, the list
is first set from the findOne
. This findOne
is leveraging the data brought over from the lists publications. In those publications (like the lists.public
publication) the Lists.publicFields
object is used to restrict which fields are sent back over to the client. When the list
is set in the above code, only the following fields are sent back to the client:
Lists.publicFields = {
name: 1,
incompleteCount: 1,
userId: 1,
};
Continuing on in the above ValidatedMethod
, we get to the list.editableBy
call. On the client the passed in this.userId
is the logged in user ID. When this parameter is passed into the editableBy
collection helper, itās compared against the userId
value that was loaded from the Lists collection above. So looking at the editableBy
helper:
...
editableBy(userId) {
if (!this.userId) {
return true;
}
return this.userId === userId;
},
...
The passed in userId
is the logged in user ID sent in from the ValidateMethod
, which is then compared against this.userId
, which in the case of a collection helper is the userId
of the loaded collection. Since we allowed userId
to be published by our Lists publications, this check can now be performed successfully. If we hadnāt allowed userId
to be published to the client from our publication, this check would fail on the client.
Thanks for the detailed explanation. What your saying all makes sense, but I canāt seem to make it work in my code. When I run findOne in my method, it works on the server but returns nothing on the client.
I thought to use publications, you need to create a subscription to it on the client. Somewhere Iām missing the glue that allows the method (in the simulation) to use findOne.
Yes - todos is doing this for lists in the main layout:
Template.App_body.onCreated(function appBodyOnCreated() {
this.subscribe('lists.public');
this.subscribe('lists.private');
...
Thanks for you patience. I think I understand now. btw: Iām using the React version of Todos.
AppContainer.jsx subscribes to lists.public. I originally thought that only the lists array passed as a prop was visible to the client. I didnāt realize that once the subscription was ready, Meteor methods on the clients could query the subset of data returned by the subscription using find or findOne.
My component is a form to edit data. I didnāt use a subscription because I donāt need the data to be reactive. I just use a method in the constructor to populate the default values. In this scenario, I assume it is more efficient just to stick with the isSimulation check.
In the absence of a React version of aldeed:autoform, what are folks using to generate forms from SimpleSchema in React?
+1
same issue - what is the most comprehensive solution to use simpleschema in React?
Regarding:
Todos = new Mongo.Collection('Todos');
Wouldnāt that create a collection called āTodosā when really the preferred Mongo convention for naming of collections is lowercase i.e. ātodosā
I feel like the correct syntax would be:
Todos = new Mongo.Collection('todos');
Thoughts?
Is this convention recorded somewhere? We felt it was simpler to name it the same as the variable.
Agreed with naming the collection the same as the variable. The Mongo convention of naming collections lowercase is simply a tutorial convention. In production, itās much better to name the collection the same as whatever JSON object is being stored as a record.
For example, if you have a well structured JSON object called āPatientā thatās been approved by standards groups such as HL7, and supported by all the major EMR vendorsā¦ you name the collection āPatientsā, not āpatientsā.
Itās the convention they use in the documentation: https://docs.mongodb.com/v3.2/reference/method/db.createCollection/
What would be the reason for using a different convention for tutorial sake to the one you would suggest people use in production?
Surely the point of a tutorial is for people to follow them in their own code base?
Furthermore, the collection āvariableā in this case as you put it is only capitalised because it is actually an instance of a class that uses the the āConstructor Invocation Patternā - and capitalisation is the correct convention only in this case. If it were in fact a simple JSON object as you suggest, the correct naming convention in JS would be camelCase.
In your specific scenario it sounds like the standards groups (HL7/EMR) in your field have chosen to contradict general JS language conventions (which I suppose is their prerogative, perhaps for consistency with older systems / DBs like SQL).
However, this discussion is around the naming conventions of MongoDB not the naming conventions of JS, which I feel are pretty clear-cut