Distinguish between assessment attempts in GQL

When caching rubric assessments in GraphQL, add the artifact attempt as
an additional key to the field, since (like many versionable objects)
different assessments can be represented by the same type and ID. This
ensures that assessments for different attempts don't stomp on each
other.

closes EVAL-2020
flag=assignments_2_student

Test plan:
- Have a course with the new student view enabled
- Have an assignment with a rubric that accepts a submission
- As a student, submit to the assignment
- As a teacher, grade the submission and assess it using the rubric
- As a student, submit to the assignment again
- As a teacher, grade and assess it again with a different score
- As a student, open the page again
  - Switch back and forth between attempts multiple times, and check
    that each attempt displays the appropriate rubric assessment,
    instead of the same assessment appearing for all attempts

Change-Id: I3db6ce226eac24d218991469bd42fe9071665242
Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/275923
Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com>
Reviewed-by: Dustin Cowles <dustin.cowles@instructure.com>
Reviewed-by: Syed Hussain <shussain@instructure.com>
QA-Review: Kai Bjorkman <kbjorkman@instructure.com>
Product-Review: Syed Hussain <shussain@instructure.com>
This commit is contained in:
Adrian Packel 2021-10-13 12:26:51 -05:00
parent 02145018be
commit 762d7d41aa
4 changed files with 39 additions and 2 deletions

View File

@ -32,6 +32,11 @@ module Types
implements Interfaces::LegacyIDInterface
field :artifact_attempt, Integer, null: false
def artifact_attempt
object.artifact_attempt || 0
end
field :assessment_type, AssessmentType, null: false
field :score, Float, null: true

View File

@ -100,4 +100,22 @@ describe Types::RubricAssessmentType do
).to eq [rubric_association.id.to_s]
end
end
describe "artifact_attempt" do
it "returns the value when artifact_attempt is non-nil" do
submission.update!(attempt: 2)
rubric_assessment.reload
rubric_assessment.update!(artifact_attempt: 2)
expect(
submission_type.resolve('rubricAssessmentsConnection { nodes { artifactAttempt } }')
).to eq [2]
end
it "returns zero when artifact_attempt is nil" do
rubric_assessment.update!(artifact_attempt: nil)
expect(
submission_type.resolve('rubricAssessmentsConnection { nodes { artifactAttempt } }')
).to eq [0]
end
end
end

View File

@ -70,13 +70,24 @@ function createCache() {
return new InMemoryCache({
addTypename: true,
dataIdFromObject: object => {
let cacheKey
if (object.id) {
return object.id
cacheKey = object.id
} else if (object._id && object.__typename) {
return object.__typename + object._id
cacheKey = object.__typename + object._id
} else {
return null
}
// Multiple distinct RubricAssessments (and likely other versionable
// objects) may be represented by the same ID and type. Add the
// artifactAttempt field to the cache key to assessments for different
// attempts don't collide.
if (object.__typename === 'RubricAssessment' && object.artifactAttempt != null) {
cacheKey = `${cacheKey}:${object.artifactAttempt}`
}
return cacheKey
},
fragmentMatcher: new IntrospectionFragmentMatcher({
introspectionQueryResultData

View File

@ -25,6 +25,7 @@ export const RubricAssessment = {
fragment: gql`
fragment RubricAssessment on RubricAssessment {
_id
artifactAttempt
assessment_type: assessmentType
assessor {
_id
@ -47,6 +48,7 @@ export const RubricAssessment = {
shape: shape({
_id: string.isRequired,
artifactAttempt: number,
assessment_type: string,
assessor: shape({
_id: string.isRequired,
@ -61,6 +63,7 @@ export const RubricAssessment = {
export const DefaultMocks = {
RubricAssessment: () => ({
_id: '1',
artifactAttempt: '1',
assessmentType: 'grading',
assessmentRatings: [{}],
score: '10'