4

I have an insert form which is created using autoform, collection2 and simple schema. The createdBy field is populated with the userId using autovalue. The form worked when using meteor.allow() for the insert but I wanted to replace the allow with a method so that I can do some validating of user roles ie make sure the user has admin rights. But now I get an error that the createdBy field is empty.

The error in dev tools is:

error: 400, reason: "Created by is required", details: undefined, message: "Created by is required [400]", errorType: "Meteor.Error"}

Courses = new Mongo.Collection('Courses');

courseSchema  = new SimpleSchema({
    title: {
        type: String,
        label: "Course Title"
    },
    description: {
        type: String,
        label: "Description"
    },
    createdAt: {
        type: Date,
        autoValue: function(){
            return new Date();
        },
        autoform:{
            type: 'hidden'
        }
    },
    startDate:{
        type: String,
        label: "Start Date"
    },
    sessions: {
        type: String,
        label: "No. of sessions"
    },
    duration: {
        type: String,
        label: "Duration of the course"
    },
    price: {
        type: String,
        label: "Course Price"
    },
    createdBy:{
        type: String,
        autoValue:function(){
            return this.userId;
        },
        autoform:{
            type:'hidden'
        }
    }
});

Courses.attachSchema(courseSchema);

The method (which is available on the client and the server):

Meteor.methods({
    addCourse: function(course){
        Courses.insert(course);
    }
});

And the template where the form is generated:

<template name="adminIndex">
   <h1>Available Courses</h1>
   {{> courseList }}    
   <button type="button" class="btn btn-success btn-block">Create New Course</button>
   <h3>Create New Course</h3>
   {{>quickForm id="InsertCourseForm" collection="Courses" type="method" meteormethod="addCourse"}}
</template>
Matthias A. Eckhart
  • 5,136
  • 4
  • 27
  • 34
Antony Castle
  • 193
  • 1
  • 11

2 Answers2

1

You need to clean the object by calling Courses.simpleSchema().clean(course); in the server method in order to add auto and default values securely. Also, please note that this.userId in your autoValue function is null for server-initiated actions, so you probably want to replace it with Meteor.userId().

In addition, you must perform your own validation by calling check(value, pattern) in the Meteor method, because client side validation can be bypassed.

For example:

if (Meteor.isServer) {
  Meteor.methods({
    addCourse: function(course) {
      Courses.simpleSchema().clean(course);
      check(course, Courses.simpleSchema());
      Courses.insert(course);
    }
  });
}
Matthias A. Eckhart
  • 5,136
  • 4
  • 27
  • 34
  • Thanks Matthias, The issue of the code being available to the client and server for the Optimistic UI to work makes sense but I was curious about the this.userId and the Meteor.userId. I'm not sure where the code is actually being run, client or server or both I guess. I assume that the method stub validates locally before updating the minimingo cache before then validating on the server before the mongo db is updated. Where is the autoValue actually run? I assumed on the client and then the details were "posted" (in HTTP thinking) to the server. Is this accurate? – Antony Castle Feb 19 '16 at 10:29
  • The schema will be validated on the client, as well as on the server. You can verify this by adding `console.log(this.userId);` inside your `autoValue` function and you will see the user ID in the browser's console and `null` in the server console. – Matthias A. Eckhart Feb 19 '16 at 11:28
  • That makes sense but I am now very confused about what autoValue does. I would have thought that it would populate the form element and the userId would then be "posted" to the server as form data. Otherwise what is the point of using autoValue. why not just add the userId to the received data in the method before inserting the data to the db? I will read up on quickform. – Antony Castle Feb 19 '16 at 12:37
  • Here's an excerpt from the [Collection2](https://github.com/aldeed/meteor-collection2#autovalue) documentation: `autoValue` _functions are run on the client only for validation purposes, but the actual value saved will always be generated on the server, regardless of whether the insert/update is initiated from the client or from the server._ – Matthias A. Eckhart Feb 19 '16 at 12:45
  • Hi Matthias, the clean is unnecessary as it is called internally by collection 2 automatically. The Meteor.userId() is eactly the thing I was looking for. Thanks for your help. – Antony Castle Feb 23 '16 at 15:16
1

So this worked but I haven't seen it used in any other examples so I have a bad feeling, but until I can find out more it will have to do:

createdBy:{
    type: String,
    autoValue:function(){
        if(Meteor.isClient){
            return this.userId;
        }else if(Meteor.isServer){
            return Meteor.userId(); 
        }
    },
Antony Castle
  • 193
  • 1
  • 11