Query node support for proficiencies/calc. methods

refs OUT-3836

flag=account_level_mastery_scales

Add top-level query node support for outcome proficiencies
and outcome calculation methods. See "Node interface" in
"app/graphql/types/HOWTO Add Types.md" for more details.

test plan:
  - create outcome proficiency and outcome calculation method
    on an account
  - query proficiency to get relay-style id:
    query MyQuery {
      outcomeProficiency(id: "<id>") {
        id
      }
    }
  - query via node and confirm the legacy id comes back:
    query MyQuery {
      node(id: "<relay-style-id>") {
        ... on OutcomeProficiency {
          _id
        }
      }
    }
  - query calculation method to get relay-style id:
    query MyQuery {
      outcomeCalculationMethod(id: "<id>") {
        id
      }
    }
  - query via node and confirm the legacy id comes back:
    query MyQuery {
      node(id: "<relay-style-id>") {
        ... on OutcomeCalculationMethod {
          _id
        }
      }
    }

Change-Id: Ibf8ebf4e2e7001481ff6450158e02b43747ba5c4
Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/245156
Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com>
QA-Review: Pat Renner <prenner@instructure.com>
Product-Review: Augusto Callejas <acallejas@instructure.com>
Reviewed-by: Evan Francis <efrancis@instructure.com>
Reviewed-by: Pat Renner <prenner@instructure.com>
This commit is contained in:
Augusto Callejas 2020-08-14 13:45:29 -10:00
parent d3cd1d7fc1
commit dcf2e37129
8 changed files with 141 additions and 11 deletions

View File

@ -63,6 +63,8 @@ class CanvasSchema < GraphQL::Schema
when Attachment then Types::FileType
when DiscussionTopic then Types::DiscussionType
when Quizzes::Quiz then Types::QuizType
when OutcomeCalculationMethod then Types::OutcomeCalculationMethodType
when OutcomeProficiency then Types::OutcomeProficiencyType
when Progress then Types::ProgressType
when Rubric then Types::RubricType
when MediaObject then Types::MediaObjectType

View File

@ -163,8 +163,18 @@ module GraphQLNodeLoader
enrollment_term
end
end
when "OutcomeCalculationMethod"
Loaders::IDLoader.for(OutcomeCalculationMethod).load(id).then do |record|
next if !record || record.deleted? || !record.context.grants_right?(ctx[:current_user], :read)
record
end
when "OutcomeProficiency"
Loaders::IDLoader.for(OutcomeProficiency).load(id).then do |record|
next if !record || record.deleted? || !record.context.grants_right?(ctx[:current_user], :read)
record
end
else
raise UnsupportedTypeError.new("don't know how to load #{type}")
raise UnsupportedTypeError, "don't know how to load #{type}"
end
end

View File

@ -32,6 +32,8 @@ class Types::LegacyNodeType < Types::BaseEnum
value "MediaObject"
value "Module"
value "ModuleItem"
value "OutcomeCalculationMethod"
value "OutcomeProficiency"
value "Page"
value "PostPolicy"
value "Progress"

View File

@ -20,6 +20,7 @@ module Types
class OutcomeCalculationMethodType < ApplicationObjectType
description 'Customized calculation method'
implements GraphQL::Types::Relay::Node
implements Interfaces::LegacyIDInterface
global_id_field :id

View File

@ -20,6 +20,7 @@ module Types
class OutcomeProficiencyType < ApplicationObjectType
description 'Customized proficiency ratings'
implements GraphQL::Types::Relay::Node
implements Interfaces::LegacyIDInterface
global_id_field :id

View File

@ -120,5 +120,23 @@ module Types
def audit_logs
Canvas::DynamoDB::DatabaseBuilder.from_config(:auditors)
end
field :outcome_calculation_method, Types::OutcomeCalculationMethodType, null: true do
description "OutcomeCalculationMethod"
argument :id, ID, "a graphql or legacy id", required: true,
prepare: GraphQLHelpers.relay_or_legacy_id_prepare_func("OutcomeCalculationMethod")
end
def outcome_calculation_method(id:)
GraphQLNodeLoader.load("OutcomeCalculationMethod", id, context)
end
field :outcome_proficiency, Types::OutcomeProficiencyType, null: true do
description "OutcomeProficiency"
argument :id, ID, "a graphql or legacy id", required: true,
prepare: GraphQLHelpers.relay_or_legacy_id_prepare_func("OutcomeProficiency")
end
def outcome_proficiency(id:)
GraphQLNodeLoader.load("OutcomeProficiency", id, context)
end
end
end

View File

@ -29,6 +29,71 @@ describe "legacyNode" do
CanvasSchema.execute(query, context: {current_user: user})
end
context "OutcomeCalculationMethod" do
before(:once) do
@calc_method = outcome_calculation_method_model(@course)
@query = <<-GQL
query {
outcomeCalculationMethod: legacyNode(type: OutcomeCalculationMethod, _id: "#{@calc_method.id}") {
... on OutcomeCalculationMethod {
_id
}
}
}
GQL
end
it "works" do
expect(
run_query(@query, @teacher)["data"]["outcomeCalculationMethod"]["_id"]
).to eq @calc_method.id.to_s
end
it "requires read permission on the course" do
original_student = @student
student_in_course(course: course_factory)
@other_class_student = @student
@student = original_student
expect(
run_query(@query, @other_class_student)["data"]["outcomeCalculationMethod"]
).to be_nil
end
end
context "OutcomeProficiency" do
before(:once) do
@proficiency = outcome_proficiency_model(@course.account)
@query = <<-GQL
query {
outcomeProficiency: legacyNode(type: OutcomeProficiency, _id: "#{@proficiency.id}") {
... on OutcomeProficiency {
_id
}
}
}
GQL
end
it "works" do
@admin = account_admin_user(account: @course.account)
expect(
run_query(@query, @admin)["data"]["outcomeProficiency"]["_id"]
).to eq @proficiency.id.to_s
end
it "requires read permission on the account" do
original_account = @account
@other_account = account_model
@admin = account_admin_user(account: @other_account)
@account = original_account
expect(
run_query(@query, @admin)["data"]["outcomeProficiency"]
).to be_nil
end
end
context "enrollments" do
before(:once) do
@enrollment = @student.enrollments.first
@ -54,6 +119,7 @@ describe "legacyNode" do
original_student = @student
student_in_course(course: course_factory)
@other_class_student = @student
@student = original_student
expect(
run_query(@query, @other_class_student)["data"]["enrollment"]
).to be_nil
@ -64,13 +130,13 @@ describe "legacyNode" do
before(:once) do
@module = @course.context_modules.create! name: "asdf"
@query = <<~GQL
query {
module: legacyNode(type: Module, _id: "#{@module.id}") {
... on Module {
_id
query {
module: legacyNode(type: Module, _id: "#{@module.id}") {
... on Module {
_id
}
}
}
}
GQL
end
@ -94,13 +160,13 @@ describe "legacyNode" do
@page = @course.wiki.front_page
@page.save!
@query = <<~GQL
query {
page: legacyNode(type: Page, _id: "#{@page.id}") {
... on Page {
_id
query {
page: legacyNode(type: Page, _id: "#{@page.id}") {
... on Page {
_id
}
}
}
}
GQL
end

View File

@ -41,6 +41,36 @@ describe Types::QueryType do
).to match_array [test_course_1, test_course_2].map(&:to_param)
end
context "OutcomeCalculationMethod" do
it "works" do
@course = Course.create! name: "TEST"
@admin = account_admin_user(account: @course.account)
@calc_method = outcome_calculation_method_model(@course.account)
expect(
CanvasSchema.execute(
"{ outcomeCalculationMethod(id: #{@calc_method.id}) { _id } }",
context: {current_user: @admin}
).dig("data", "outcomeCalculationMethod", "_id")
).to eq @calc_method.id.to_s
end
end
context "OutcomeProficiency" do
it "works" do
@course = Course.create! name: "TEST"
@admin = account_admin_user(account: @course.account)
@proficiency = outcome_proficiency_model(@course.account)
expect(
CanvasSchema.execute(
"{ outcomeProficiency(id: #{@proficiency.id}) { _id } }",
context: {current_user: @admin}
).dig("data", "outcomeProficiency", "_id")
).to eq @proficiency.id.to_s
end
end
context "sisId" do
let_once(:generic_sis_id) { "di_ecruos_sis" }
let_once(:course) { Course.create!(name: "TEST", sis_source_id: generic_sis_id, account: account) }