add edited_at to submission_comments

closes GRADE-303

Test Plan:
1. Verify the migration succeeds, and verify you can also roll the
   migration back successfully. The rest of the steps will assume the
   migration has been run.
2. Create a course with an assignment. Submit to that assignment as a
   student, and comment on the submission as the student.
3. Make an API call to get the submissions and their comments for the
   assignment.

     GET api/v1/courses/:course_id/assignments/:id/submissions?
         include[]=submission_comments

   Verify the submission comments that are returned have an 'edited_at'
   key on them. The value of 'edited_at' should be nil for all comments.

4. Enter a rails console and update the 'comment' attribute on the
   submission comment.

   assignment = Assignment.find(<your-assignment-id>)
   student = User.find(<your-student-id>)
   submission = assignment.submissions.find_by(user_id: student)
   comment = submission.submission_comments.first
   comment.update!(comment: "i am updating the text on the comment!")

5. Make an API call to get the submissions and their comments for the
   assignment.

     GET api/v1/courses/:course_id/assignments/:id/submissions?
         include[]=submission_comments

   Verify the value of the 'edited_at' attribute for the comment you
   adjusted in step 4 is a timestamp.

Change-Id: I4c91fdfd7a9cef194f08a2d086601fb827a50095
Reviewed-on: https://gerrit.instructure.com/127476
Tested-by: Jenkins
Reviewed-by: Derek Bender <djbender@instructure.com>
Reviewed-by: Shahbaz Javeed <sjaveed@instructure.com>
QA-Review: Anju Reddy <areddy@instructure.com>
Product-Review: Keith T. Garner <kgarner@instructure.com>
Reviewed-by: Rob Orton <rob@instructure.com>
This commit is contained in:
Spencer Olson 2017-09-26 17:04:44 -05:00
parent 81d775bd25
commit e1d4b3ee4d
9 changed files with 229 additions and 89 deletions

View File

@ -78,6 +78,10 @@ require 'action_controller_test_process'
# "example": "2012-01-01T01:00:00Z",
# "type": "datetime"
# },
# "edited_at" : {
# "example": "2012-01-02T01:00:00Z",
# "type": "datetime"
# },
# "media_comment": {
# "$ref": "MediaComment"
# }

View File

@ -31,6 +31,7 @@ class SubmissionComment < ActiveRecord::Base
validates_length_of :comment, :minimum => 1, :allow_nil => true, :allow_blank => true
before_save :infer_details
before_save :set_edited_at
after_save :update_participation
after_save :check_for_media_object
after_update :publish_other_comments_in_this_group
@ -288,4 +289,10 @@ class SubmissionComment < ActiveRecord::Base
def skip_group_callbacks?
!!@skip_group_callbacks
end
def set_edited_at
if comment_changed? && comment_was.present?
self.edited_at = Time.zone.now
end
end
end

View File

@ -0,0 +1,24 @@
#
# Copyright (C) 2017 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
class AddEditedAtToSubmissionComments < ActiveRecord::Migration[5.0]
tag :predeploy
def change
add_column :submission_comments, :edited_at, :datetime
end
end

View File

@ -21,7 +21,7 @@ module Api::V1::SubmissionComment
def submission_comment_json(submission_comment, user)
sc_hash = submission_comment.as_json(
:include_root => false,
:only => %w(id author_id author_name created_at comment)
:only => %w(id author_id author_name created_at edited_at comment)
)
if submission_comment.media_comment?
@ -52,6 +52,4 @@ module Api::V1::SubmissionComment
def submission_comments_json(submission_comments, user)
submission_comments.map{ |submission_comment| submission_comment_json(submission_comment, user) }
end
end
end

View File

@ -228,22 +228,30 @@ describe PeerReviewsApiController, type: :request do
end
def assessment_with_comments(comment)
{"assessor_id"=>@student2.id,
"asset_id"=>@submission.id,
"asset_type"=>"Submission",
"id"=>@assessment_request.id,
"submission_comments" => [{"author_id"=>@student2.id,
"author_name"=>@student2.name,
"comment"=>comment.comment,
"created_at"=>comment.created_at.as_json,
"id"=>comment.id,
"author"=>{"id"=>@student2.id,
"display_name"=>@student2.name,
"avatar_image_url"=>"http://www.example.com/images/messages/avatar-50.png",
"html_url"=>"http://www.example.com/courses/#{@course.id}/users/#{@student2.id}"}}],
"user_id"=>@student1.id,
"workflow_state"=>@assessment_request.workflow_state}
{
"assessor_id" => @student2.id,
"asset_id" => @submission.id,
"asset_type" => "Submission",
"id" => @assessment_request.id,
"submission_comments" => [
{
"author_id" => @student2.id,
"author_name" => @student2.name,
"comment" => comment.comment,
"created_at" => comment.created_at.as_json,
"edited_at" => nil,
"id" => comment.id,
"author" => {
"id" => @student2.id,
"display_name" => @student2.name,
"avatar_image_url" => "http://www.example.com/images/messages/avatar-50.png",
"html_url" => "http://www.example.com/courses/#{@course.id}/users/#{@student2.id}"
}
}
],
"user_id" => @student1.id,
"workflow_state" => @assessment_request.workflow_state
}
end
context 'with admin_context' do
@ -393,18 +401,27 @@ describe PeerReviewsApiController, type: :request do
@user = @student1
json = api_call(:get, @resource_path, @resource_params, { :include => %w(submission_comments) })
expect(json.count).to eq(1)
expect(json[0]).to eq({"asset_id"=>@submission.id,
"asset_type"=>"Submission",
"id"=>@assessment_request.id,
"submission_comments" => [{"author_id"=>nil,
"author_name"=>"Anonymous User",
'avatar_path' => User.default_avatar_fallback,
"comment"=>"review comment",
"created_at"=>@comment.created_at.as_json,
"id"=>@comment.id,
"author"=>{}}],
"user_id"=>@student1.id,
"workflow_state"=>@assessment_request.workflow_state})
expect(json[0]).to eq(
{
"asset_id"=>@submission.id,
"asset_type"=>"Submission",
"id"=>@assessment_request.id,
"submission_comments" => [
{
"author_id" => nil,
"author_name" => "Anonymous User",
"avatar_path" => User.default_avatar_fallback,
"comment" => "review comment",
"created_at" => @comment.created_at.as_json,
"edited_at" => nil,
"id" => @comment.id,
"author" => {}
}
],
"user_id"=>@student1.id,
"workflow_state"=>@assessment_request.workflow_state
}
)
end
it 'should return peer reviews for a specific submission' do

View File

@ -393,35 +393,38 @@ describe UsersController, type: :request do
'url' => nil,
'user_id' => @sub.user_id,
'submission_comments' => [{
'body' => 'c1',
'comment' => 'c1',
'author' => {
'id' => @teacher.id,
'display_name' => 'teacher',
'html_url' => "http://www.example.com/courses/#{@course.id}/users/#{@teacher.id}",
'avatar_image_url' => User.avatar_fallback_url(nil, request)
'submission_comments' => [
{
'body' => 'c1',
'comment' => 'c1',
'author' => {
'id' => @teacher.id,
'display_name' => 'teacher',
'html_url' => "http://www.example.com/courses/#{@course.id}/users/#{@teacher.id}",
'avatar_image_url' => User.avatar_fallback_url(nil, request)
},
'author_name' => 'teacher',
'author_id' => @teacher.id,
'created_at' => @sub.submission_comments[0].created_at.as_json,
'edited_at' => nil,
'id' => @sub.submission_comments[0].id
},
'author_name' => 'teacher',
'author_id' => @teacher.id,
'created_at' => @sub.submission_comments[0].created_at.as_json,
'id' => @sub.submission_comments[0].id
},
{
'body' => 'c2',
'comment' => 'c2',
'author' => {
'id' => @user.id,
'display_name' => 'User',
'html_url' => "http://www.example.com/courses/#{@course.id}/users/#{@user.id}",
'avatar_image_url' => User.avatar_fallback_url(nil, request)
},
'author_name' => 'User',
'author_id' => @user.id,
'created_at' => @sub.submission_comments[1].created_at.as_json,
'id' => @sub.submission_comments[1].id
},],
{
'body' => 'c2',
'comment' => 'c2',
'author' => {
'id' => @user.id,
'display_name' => 'User',
'html_url' => "http://www.example.com/courses/#{@course.id}/users/#{@user.id}",
'avatar_image_url' => User.avatar_fallback_url(nil, request)
},
'author_name' => 'User',
'author_id' => @user.id,
'created_at' => @sub.submission_comments[1].created_at.as_json,
'edited_at' => nil,
'id' => @sub.submission_comments[1].id
}
],
'course' => {
'name' => @course.name,
'end_at' => @course.end_at,
@ -514,35 +517,38 @@ describe UsersController, type: :request do
'url' => nil,
'user_id' => @sub.user_id,
'submission_comments' => [{
'body' => 'c1',
'comment' => 'c1',
'author' => {
'id' => @teacher.id,
'display_name' => 'teacher',
'html_url' => "http://www.example.com/courses/#{@course.id}/users/#{@teacher.id}",
'avatar_image_url' => User.avatar_fallback_url(nil, request)
'submission_comments' => [
{
'body' => 'c1',
'comment' => 'c1',
'author' => {
'id' => @teacher.id,
'display_name' => 'teacher',
'html_url' => "http://www.example.com/courses/#{@course.id}/users/#{@teacher.id}",
'avatar_image_url' => User.avatar_fallback_url(nil, request)
},
'author_name' => 'teacher',
'author_id' => @teacher.id,
'created_at' => @sub.submission_comments[0].created_at.as_json,
'edited_at' => nil,
'id' => @sub.submission_comments[0].id
},
'author_name' => 'teacher',
'author_id' => @teacher.id,
'created_at' => @sub.submission_comments[0].created_at.as_json,
'id' => @sub.submission_comments[0].id
},
{
'body' => 'c2',
'comment' => 'c2',
'author' => {
'id' => @user.id,
'display_name' => 'User',
'html_url' => "http://www.example.com/courses/#{@course.id}/users/#{@user.id}",
'avatar_image_url' => User.avatar_fallback_url(nil, request)
},
'author_name' => 'User',
'author_id' => @user.id,
'created_at' => @sub.submission_comments[1].created_at.as_json,
'id' => @sub.submission_comments[1].id
},],
{
'body' => 'c2',
'comment' => 'c2',
'author' => {
'id' => @user.id,
'display_name' => 'User',
'html_url' => "http://www.example.com/courses/#{@course.id}/users/#{@user.id}",
'avatar_image_url' => User.avatar_fallback_url(nil, request)
},
'author_name' => 'User',
'author_id' => @user.id,
'created_at' => @sub.submission_comments[1].created_at.as_json,
'edited_at' => nil,
'id' => @sub.submission_comments[1].id
}
],
'course' => {
'name' => @course.name,
'end_at' => @course.end_at,

View File

@ -778,6 +778,7 @@ describe 'Submissions API', type: :request do
"display_name" => nil
},
"created_at"=>comment.created_at.as_json,
"edited_at" => nil,
"author"=>{
"id" => @teacher.id,
"display_name" => "User",
@ -1090,6 +1091,7 @@ describe 'Submissions API', type: :request do
"display_name" => nil
},
"created_at"=>comment.reload.created_at.as_json,
"edited_at" => nil,
"author"=>{
"id" => @teacher.id,
"display_name" => "User",

View File

@ -0,0 +1,49 @@
#
# Copyright (C) 2017 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
require "spec_helper"
describe Api::V1::SubmissionComment do
before(:once) do
course = Course.create!
@student = User.create!
course.enroll_student(@student, active_all: true)
assignment = course.assignments.create!
submission = assignment.submissions.find_by(user_id: @student)
@submission_comment = submission.submission_comments.create!
@comment = Object.new.extend(Api::V1::SubmissionComment, Api::V1::User)
end
let(:comment_json) { @comment.submission_comment_json(@submission_comment, @student) }
describe "#submission_comment_json" do
it "includes the 'edited_at' key" do
expect(comment_json).to have_key "edited_at"
end
it "'edited_at' is set if the submission comment has been edited" do
now = Time.zone.now
@submission_comment.edited_at = now
expect(comment_json[:edited_at]).to be now
end
it "'edited_at' is nil if the submission comment has not been edited" do
expect(comment_json[:edited_at]).to be_nil
end
end
end

View File

@ -33,7 +33,7 @@ describe SubmissionComment do
end
it "should create a new instance given valid attributes" do
SubmissionComment.create!(@valid_attributes)
expect { SubmissionComment.create!(@valid_attributes) }.not_to raise_error
end
describe 'notifications' do
@ -454,4 +454,37 @@ This text has a http://www.google.com link in it...
end
end
end
describe "#edited_at" do
before(:once) do
@comment = SubmissionComment.create!(@valid_attributes)
end
it "is nil for newly-created submission comments" do
expect(@comment.edited_at).to be_nil
end
it "remains nil if the submission comment is updated but the 'comment' attribute is unchanged" do
@comment.update!(draft: true, hidden: true)
expect(@comment.edited_at).to be_nil
end
it "is set if the 'comment' attribute is updated on the submission comment" do
now = Time.zone.now
Timecop.freeze(now) { @comment.update!(comment: "changing the comment!") }
expect(@comment.edited_at).to eql now
end
it "is updated on subsequent changes to the 'comment' attribute" do
now = Time.zone.now
Timecop.freeze(now) { @comment.update!(comment: "changing the comment!") }
later = 2.minutes.from_now(now)
Timecop.freeze(later) do
expect { @comment.update!(comment: "and again, changing it!") }.to change {
@comment.edited_at
}.from(now).to(later)
end
end
end
end