To ensure the type of the arguments my publications receive, should I use SimpleSchema or check()?
Meteor.publish('todos.inList', function(listId, limit) {
new SimpleSchema({
listId: { type: String },
limit: { type: Number }
}).validate({ listId, limit });
[...]
});
or
Meteor.publish('todos.inList', function(listId, limit) {
check(listId, String);
check (limit, Number);
[...]
});
check() allows you to check data type, which is one thing, but is somewhat limited.
SimpleSchema is much more powerful as it checks all keys in a documents (instead of one at a time) and lets you define not only type but also allowed values, define default (or dynamic) values when not present.
You should use SimpleSchema this way:
mySchema = new SimpleSchema({ <your schema here>});
var MyCollection = new Mongo.Collection("my_collection");
MyCollection.attachSchema(mySchema);
That way, you don't event need to check the schema in methods: it will be done automatically.
Of course it's always good practice to use the
mySchema.validate(document);
to validate a client generated document before inserting it in your collection, but if you don't and your document doesn't match the schema (extra keys, wrong types etc...) SimpleSchema will reject the parts that don't belong.
To check arguments to a publish() function or a Meteor.method() use check(). You define a SimpleSchema to validate inserts, upserts, and updates to collections. A publication is none of those - it's readonly. Your use of SimpleSchema with .validate() inline would actually work but it's a pretty unusual pattern and a bit of overkill.
You might find this helpful.
CHECK is a lightweight package for argument checking and general pattern matching. where as SimpleSchema is a huge package with one of the check features. It is just that one package was made before the other.
Both works the same. You can use CHECK externally in Meteor.methods as well. Decision is all yours.
Michel Floyd answer's, made me realize that check() actually sends Meteor.Error(400, "Match Failed") to the client, while SimpleSchema within Methods sends detailed ValidationError one can act upon to display appropriate error messages on a form for instance.
So to answer the question : should we use check() or SimpleSchema() to assess our arguments types in Meteor, I believe the answer is :
Use SimpleSchema if you need a detailed report of the error from the client, otherwise check() is the way to go not to send back critical info.
Related
I am trying to call an external API from an Excel on web. However, I am stuck on trying to get the result from the fetch call. I am even using the Office doc example to make sure
From an Excel, click on Automate to create a new script
async function main(workbook: ExcelScript.Workbook): Promise<void> {
let fetchResult = await fetch('https://jsonplaceholder.typicode.com/todos/1');
let json = await fetchResult.json();
}
I keep on getting the following message (at the fetchResult.json() call)
"Office Scripts cannot infer the data type of this variable or inferring it might result in unexpected errors. Please annotate the type of the variable to avoid this error. You can also use the Quick fix option provided in the editor to auto fill the type based on the usage. Quick Fix can be accessed by right clicking on the variable name and selecting Quick Fix link."
When running the Chrome inspector, the API request seems to be on hold "CAUTION: request is not finished yet"
PS: I am not the Office administrator and is not reachable right now, but hoping this is not a problem with my user or the Office account configuration
Any idea what the problem might be?
Thanks!
"any" types not being allowed in OfficeScript is by design. We think any types in general can lead to developer errors. I understand it can be hard to declare types – but these days most popular APIs provide you the interface (or d.ts) that you can use.
Secondly, there are tools such as https://quicktype.io/typescript where you can type in your sample JSON and it’ll give you the full interface which you can then declare in your code using interface keyword.
See this code for example: https://github.com/sumurthy/officescripts-projects/blob/main/API%20Calls/APICall.ts
You don’t need to declare all properties – only the ones you’ll use.
It’s more up-front work – but in the end the quality is better.
Adding an interface definition for the expected JSON type fixed the problem for me.
interface Todo {
userId: number;
id: number;
title: string;
completed: boolean
}
async function main(workbook: ExcelScript.Workbook): Promise<void> {
let fetchResult = await fetch('https://jsonplaceholder.typicode.com/todos/1');
let json: Todo = await fetchResult.json();
console.log(json);
}
You may need to define a different interface if the Web API you're calling returns different data structure.
I have a publication that returns a cursor that looks like this:
// Publication RETURNS THIS QUERY
Notepads.find({_id: notepadId, $or: [{userId: this.userId}, {'collaborators.userId': this.userId}], archived: false})
As you can see the query is unique to the user since it includes this.userId
I use this.userId in the publication as a form of security. You will only get back data if you are associated with that particular notepad.
In my app, multiple people can collaborate on a single notepad. So to make the observer more reusable will this adjustment help my app?
// Optimized publication
notepad = Notepads.findOne({_id: notepadId, $or: [{userId: #userId}, {'collaborators.userId': #userId}], archived: false})
if notepad?
// Optimized publication RETURNS THIS QUERY
return Notepads.find({_id: notepad._id, archived: false})
else
return
I think this is what observer reuse means. That the publication returns the exact same query for any user that subscribes to it. So is this correct, is this optimization worth the change?
There are problem with your optimized version that is it is not reactive. Because you use Notepads.findOne as a security check to make sure user have access to the document.
Inside publication .findOne is not reactive, say at the time the publication is executed user have no access to the document so nothing is sent to client, then user is added as an collaborator but that makes no change because the publication will re re-run.
Quoting from kadira.io on How Observers are Handled in Meteor:
In order to create identical observers, you need to create cursors
with:
the same collection
the same selector (query)
the same set of options (including sort, limit, etc.)
In your example the selector is not the same for different users.
Selectors are Object Literals and are evaluated before being passed into the .find call. This means that {_id: notepad._id, archived: false} becomes
{_id: 'myNotebookID', archived: false}, or
{_id: 'anotherNotebookId', archived: false}.
As you can see, these are different selectors after you resolve notepad._id, and this happens before being passed to .find().
If your first db query had returned the same notepad because your second user was a collaborator on the first user's notebook, you would end up with the same selector, and a single cursor / observable. However for most apps this is not likely to be common enough to optimise for, especially as (as #Khang points out) you are going to lose reactivity.
So, it's not the same query, it's not going to reuse the observer, and is not worth the change.
I'm doing the newbie tutorial 'simple-todo' and noticed that once I added security in step 9, I was no longer able to delete tasks created before that.
The issue is that my remove method is checking to make sure that the ID it receives is a string, and the to-do tasks that were made earlier via the console return an object when I use this_.id.
In other words:
Tasks created via the terminal, this._id -> ObjectId("57a128afbe5fd7e7ba9a6fca")
Tasks created with the Tasks.insert method, this._id -> "57a128afbe5fd7e7ba9a6fca"
And the new remove method doesn't like the ObjectId part. How can I get just the ID? I would figure it'd be something like this._id._id, but that's undefined. The workaround was to remove the check from the "remove" method, which is less secure.
Link: https://www.meteor.com/tutorials/blaze/security-with-methods
You can use this._id._str to get the Hex part of the ObjectId.
I would suggest that your method only uses the string, and do a check in the client to see if you need to use this._id or this._id._str
Being new to javascript and surly baby in Meteor. I could not make sense reading this part of the docs. about Meteor.publish(name, func)
Arguments
name String
Name of the record set. If null, the set has no name, and the record set is automatically sent to all connected clients.
I take it that record set means Meteor Collection, if that is correct, then how can a publish action take place on a collection with name "null" or even a collection with no name as stated? I mean, where is the collection to publish if the first parameter "that collection name" is null or does not exist? Thanks
The name parameter in Meteor.publish has absolutely nothing to do with the collection. While the convention is that you should have similar naming to what collection(s) you're using, you could literally call a publish function "asdjfsaidfj" and it would be valid. As yudap said, what data you're sending to the client is entirely determined in the function. You can also return data from multiple collections using an array:
return [
ExampleCollection.find(),
AnotherCollection.find()
];
If you publish data with null argument:
Meteor.publish(null, func)
Basically it is the same as you autopublish without autopublish package. That's mean you don't need to subscribe and you don't need to install autopublish package. The data is ready in client and reactive and you can use it in whatever template without subscribing.
where is the collection to publish? Whatever collection you want to autopublish. Just define it in func:
Meteor.publish(null, function () {
CollectionName.find({}, {
/*
sort: Sort specifier,
skip: Number,
limit: Number,
fields: Field specifier,
reactive: Boolean,
transform: Function
*/
});
});
I have a question where all the parameters for the meteor functions are coming from? Things like postAttribues, id, postId, limit, etc, etc...
Meteor.publish('newPosts', function(limit) {
return Posts.find({}, {sort: {submitted: -1}, limit: limit});
});
Meteor.publish('singlePost', function(id) {
return id && Posts.find(id);
});
//related to post
Meteor.publish('comments', function(postId) {
return Comments.find({postId: postId});
});
Are they signaled from Mongo DB? It's fine and dandy to memorize these things, but it would be nice to know where these parameters are coming from and what parameters are usually available to me.
I never used any frameworks before, so this may be why I'm confused. I worked exclusively with Javascript before jumping on Meteor.
I also have the same question about Iron Router: When creating a route, we can set a route with a specific Id with /randomName/:_id and the unique code that's responsible for associating the ":_Id" with the actual page is this.params._id. Why and how does the back end associate these things?
I would appreciate any help to help me understand this better.
A meteor find() query follows the syntax find({query}, {options}) defined here: http://docs.meteor.com/#/full/find where the options parameter is an object containing sort, limit, etc... These options look similar to some Mongo operators such as .sort() and .limit() but are defined
The parameters limit and sort are part of the options parameter. It would be useful to review the documentation for Meteor found here: https://docs.mongodb.org/manual/
The parameter postId comes from the way you have defined your objects in your DB. This field is part of your query parameter which specifies what exactly to find in the DB. So by specifying a postId:, Meteor will look through your Comments collection for any containing the postId that you pass. When you pass a string as the query parameter, it is expected that that string is an _id in your collection.
For the parameters being passed into the publication itself see docs.meteor.com/#/full/meteor_subscribe . It comes from the subscription. Basically, you can pass. Parameters between the client and the server this way. To make your publication more robust, you can add parameters as you wish so that the client can specify which 'id' or 'limit' that they want.
As for your iron:router question, I am not sure exactly what you are asking about how the backend associates parameters and the page itself. Perhaps you could be more specific and update your question accordingly