Resolve outcome friendly descriptions via the account hierarchy
closes OUT-4402 flag=outcomes_friendly_description Test plan: - Create 2 outcomes (outcome 1 and 2) in a course Make sure you have those 2 outcomes inside the same group to make the test easier - Create an outcome friendly description for: outcome 1: for the course and for the account outcome 2: just for the account can create using the snippet in console OutcomeFriendlyDescription.create!({ learning_outcome: outcome, context: course || account, description: "account or course description" }) - Open graphiql and the queries (replace THE_GROUP_ID, THE_COURSE_ID and THE_ACCOUNT_ID): { legacyNode(type: LearningOutcomeGroup, _id: THE_GROUP_ID) { ... on LearningOutcomeGroup { outcomes(first: 2) { nodes { ... on LearningOutcome { _id title friendlyDescription( contextId: THE_COURSE_ID contextType: "Course" ) { _id description } } } } } } } Make sure you see an account friendlyDescription and a course friendlyDescription { legacyNode(type: LearningOutcomeGroup, _id: THE_GROUP_ID) { ... on LearningOutcomeGroup { outcomes(first: 2) { nodes { ... on LearningOutcome { _id title friendlyDescription( contextId: THE_ACCOUNT_ID contextType: "Account" ) { _id description } } } } } } } Make sure you see only account friendlyDescriptions for both outcomes Change-Id: I809422d8762c961ec4e5586fce645e9911ca22ff Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/267852 Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com> Reviewed-by: Augusto Callejas <acallejas@instructure.com> QA-Review: Martin Yosifov <martin.yosifov@instructure.com> Product-Review: Augusto Callejas <acallejas@instructure.com>
This commit is contained in:
parent
d43feb92d4
commit
8675815425
|
@ -19,22 +19,72 @@
|
|||
#
|
||||
|
||||
class Loaders::OutcomeFriendlyDescriptionLoader < GraphQL::Batch::Loader
|
||||
VALID_CONTEXT_TYPES = ['Course', 'Account'].freeze
|
||||
|
||||
def initialize(context_id, context_type)
|
||||
@context_id = context_id
|
||||
@context_type = context_type
|
||||
end
|
||||
|
||||
def perform(outcome_ids)
|
||||
OutcomeFriendlyDescription.active.where(
|
||||
learning_outcome_id: outcome_ids,
|
||||
context_id: @context_id,
|
||||
context_type: @context_type,
|
||||
).each do |friendly_description|
|
||||
fulfill(friendly_description.learning_outcome_id, friendly_description)
|
||||
def context_queries
|
||||
queries = []
|
||||
|
||||
if @course
|
||||
queries << OutcomeFriendlyDescription.sanitize_sql([
|
||||
"context_type = 'Course' AND context_id = ?", @course.id
|
||||
])
|
||||
end
|
||||
|
||||
queries << OutcomeFriendlyDescription.sanitize_sql([
|
||||
"context_type = 'Account' AND context_id IN (?)", @account.account_chain_ids
|
||||
])
|
||||
|
||||
'(' + queries.join(') OR (') + ')'
|
||||
end
|
||||
|
||||
def valid_context?
|
||||
return false unless VALID_CONTEXT_TYPES.include?(@context_type)
|
||||
|
||||
@context = @context_type.constantize.active.find_by(id: @context_id)
|
||||
return false unless @context
|
||||
|
||||
if @context.is_a?(Course)
|
||||
@course = @context
|
||||
@account = @context.account
|
||||
else
|
||||
@account = @context
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
def nullify_resting(outcome_ids)
|
||||
outcome_ids.each do |outcome_id|
|
||||
fulfill(outcome_id, nil) unless fulfilled?(outcome_id)
|
||||
end
|
||||
end
|
||||
|
||||
def perform(outcome_ids)
|
||||
unless valid_context?
|
||||
nullify_resting(outcome_ids)
|
||||
return
|
||||
end
|
||||
|
||||
account_order = @account.account_chain_ids
|
||||
|
||||
# get all friendly description for the course and all parent accounts once
|
||||
# sort by course, then the account parent order in account_chain_ids
|
||||
# and fulfill every friendly description
|
||||
friendly_descriptions = OutcomeFriendlyDescription.active.where(
|
||||
learning_outcome_id: outcome_ids
|
||||
).where(context_queries).to_a.sort_by do |friendly_description|
|
||||
friendly_description.context_type == 'Course' ? 0 : account_order.index(friendly_description.context_id) + 1
|
||||
end
|
||||
|
||||
friendly_descriptions.each do |friendly_description|
|
||||
outcome_id = friendly_description.learning_outcome_id
|
||||
fulfill(outcome_id, friendly_description) unless fulfilled?(outcome_id)
|
||||
end
|
||||
|
||||
nullify_resting(outcome_ids)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,166 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
# Copyright (C) 2021 - 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 File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
||||
|
||||
describe Loaders::OutcomeFriendlyDescriptionLoader do
|
||||
before do
|
||||
course_with_student(active_all: true)
|
||||
outcome_model(context: @course)
|
||||
@course_account = @course.account
|
||||
@parent_account = account_model
|
||||
@course_account.parent_account = @parent_account
|
||||
@course_account.save!
|
||||
end
|
||||
|
||||
def create_course_fd
|
||||
@course_fd = OutcomeFriendlyDescription.create!({
|
||||
learning_outcome: @outcome,
|
||||
context: @course,
|
||||
description: "course's description"
|
||||
})
|
||||
end
|
||||
|
||||
def create_account_fd
|
||||
@account_fd = OutcomeFriendlyDescription.create!({
|
||||
learning_outcome: @outcome,
|
||||
context: @course_account,
|
||||
description: "account's description"
|
||||
})
|
||||
end
|
||||
|
||||
def create_parent_account_fd
|
||||
@parent_account_fd = OutcomeFriendlyDescription.create!({
|
||||
learning_outcome: @outcome,
|
||||
context: @parent_account,
|
||||
description: "parent account's description"
|
||||
})
|
||||
end
|
||||
|
||||
it "prioritizes course fd" do
|
||||
create_course_fd
|
||||
create_account_fd
|
||||
create_parent_account_fd
|
||||
|
||||
GraphQL::Batch.batch do
|
||||
fd_loader = Loaders::OutcomeFriendlyDescriptionLoader.for(
|
||||
@course.id, 'Course'
|
||||
)
|
||||
fd_loader.load(@outcome.id).then { |fd|
|
||||
expect(fd).to eq @course_fd
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
it "ignores course fd if we're loading for the account" do
|
||||
create_course_fd
|
||||
create_account_fd
|
||||
create_parent_account_fd
|
||||
|
||||
GraphQL::Batch.batch do
|
||||
fd_loader = Loaders::OutcomeFriendlyDescriptionLoader.for(
|
||||
@course_account.id, 'Account'
|
||||
)
|
||||
fd_loader.load(@outcome.id).then { |fd|
|
||||
expect(fd).to eq @account_fd
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
it "resolves account fd when there isn't any course fd" do
|
||||
create_account_fd
|
||||
|
||||
GraphQL::Batch.batch do
|
||||
fd_loader = Loaders::OutcomeFriendlyDescriptionLoader.for(
|
||||
@course.id, 'Course'
|
||||
)
|
||||
fd_loader.load(@outcome.id).then { |fd|
|
||||
expect(fd).to eq @account_fd
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
it "resolves to parent account fd when there isn't any course and account fd" do
|
||||
create_parent_account_fd
|
||||
|
||||
GraphQL::Batch.batch do
|
||||
fd_loader = Loaders::OutcomeFriendlyDescriptionLoader.for(
|
||||
@course.id, 'Course'
|
||||
)
|
||||
fd_loader.load(@outcome.id).then { |fd|
|
||||
expect(fd).to eq @parent_account_fd
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
it "resolves to parent account fd when there isn't any account fd" do
|
||||
create_parent_account_fd
|
||||
|
||||
GraphQL::Batch.batch do
|
||||
fd_loader = Loaders::OutcomeFriendlyDescriptionLoader.for(
|
||||
@account.id, 'Account'
|
||||
)
|
||||
fd_loader.load(@outcome.id).then { |fd|
|
||||
expect(fd).to eq @parent_account_fd
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
it "resolves to nil when there is no fd associated with the outcome" do
|
||||
create_course_fd
|
||||
create_account_fd
|
||||
create_parent_account_fd
|
||||
|
||||
GraphQL::Batch.batch do
|
||||
fd_loader = Loaders::OutcomeFriendlyDescriptionLoader.for(
|
||||
@course.id, 'Course'
|
||||
)
|
||||
fd_loader.load(@outcome.id + 1).then { |fd|
|
||||
expect(fd).to be_nil
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
it "resolves to nil when passsing invalid context type" do
|
||||
create_course_fd
|
||||
|
||||
GraphQL::Batch.batch do
|
||||
fd_loader = Loaders::OutcomeFriendlyDescriptionLoader.for(
|
||||
@course.id, 'InvalidContextType'
|
||||
)
|
||||
fd_loader.load(@outcome.id).then { |fd|
|
||||
expect(fd).to be_nil
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
it "resolves to nil when passsing invalid context id" do
|
||||
create_course_fd
|
||||
|
||||
GraphQL::Batch.batch do
|
||||
fd_loader = Loaders::OutcomeFriendlyDescriptionLoader.for(
|
||||
@course.id + 99, 'Course'
|
||||
)
|
||||
fd_loader.load(@outcome.id).then { |fd|
|
||||
expect(fd).to be_nil
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
|
@ -101,4 +101,19 @@ describe Types::LearningOutcomeType do
|
|||
.to eq true
|
||||
end
|
||||
end
|
||||
|
||||
context "friendlyDescription" do
|
||||
let(:course) { Course.create! }
|
||||
|
||||
it "resolves friendly description correctly" do
|
||||
course_fd = OutcomeFriendlyDescription.create!({
|
||||
learning_outcome: @outcome,
|
||||
context: course,
|
||||
description: "course's description"
|
||||
})
|
||||
|
||||
expect(outcome_type.resolve("friendlyDescription(contextType: \"Course\", contextId: #{course.id}) { _id }"))
|
||||
.to eq course_fd.id.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue