From 84a381583586caa68e620ab88d79bc3ccd484bc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Geonizeli?= Date: Thu, 21 Jul 2022 14:26:03 -0300 Subject: [PATCH] add questions query --- app/controllers/graphql_controller.rb | 3 +- .../enums/question_bloom_taxonomy_enum.rb | 9 ++++++ app/graphql/enums/question_check_type_enum.rb | 9 ++++++ app/graphql/enums/question_difficulty_enum.rb | 9 ++++++ app/graphql/enums/question_status_enum.rb | 9 ++++++ app/graphql/inputs/date_range_input.rb | 8 +++++ app/graphql/inputs/question_where_input.rb | 15 +++++++++ app/graphql/progress_test_schema.rb | 11 ++++--- .../resolvers/questions_query_resolver.rb | 32 +++++++++++++++++++ app/graphql/types/base_enum.rb | 5 +++ app/graphql/types/query_type.rb | 12 +++---- .../types/question_alternative_type.rb | 12 +++++++ app/graphql/types/question_type.rb | 28 ++++++++++++++++ app/policies/application_policy.rb | 5 +++ app/policies/question_policy.rb | 24 ++++++++++++++ 15 files changed, 177 insertions(+), 14 deletions(-) create mode 100644 app/graphql/enums/question_bloom_taxonomy_enum.rb create mode 100644 app/graphql/enums/question_check_type_enum.rb create mode 100644 app/graphql/enums/question_difficulty_enum.rb create mode 100644 app/graphql/enums/question_status_enum.rb create mode 100644 app/graphql/inputs/date_range_input.rb create mode 100644 app/graphql/inputs/question_where_input.rb create mode 100644 app/graphql/resolvers/questions_query_resolver.rb create mode 100644 app/graphql/types/question_alternative_type.rb create mode 100644 app/graphql/types/question_type.rb create mode 100644 app/policies/question_policy.rb diff --git a/app/controllers/graphql_controller.rb b/app/controllers/graphql_controller.rb index 016713e..67599d2 100644 --- a/app/controllers/graphql_controller.rb +++ b/app/controllers/graphql_controller.rb @@ -9,8 +9,7 @@ class GraphqlController < ApplicationController query = params[:query] operation_name = params[:operationName] context = { - # Query context goes here, for example: - # current_user: current_user, + current_user: current_user, } result = ProgressTestSchema.execute(query, variables: variables, context: context, operation_name: operation_name) render json: result diff --git a/app/graphql/enums/question_bloom_taxonomy_enum.rb b/app/graphql/enums/question_bloom_taxonomy_enum.rb new file mode 100644 index 0000000..e1ae7c6 --- /dev/null +++ b/app/graphql/enums/question_bloom_taxonomy_enum.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module Enums + class QuestionBloomTaxonomyEnum < Types::BaseEnum + graphql_name "QuestionBloomTaxonomy" + + values_from_enumerize(Question.bloom_taxonomy) + end +end diff --git a/app/graphql/enums/question_check_type_enum.rb b/app/graphql/enums/question_check_type_enum.rb new file mode 100644 index 0000000..5b1daee --- /dev/null +++ b/app/graphql/enums/question_check_type_enum.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module Enums + class QuestionCheckTypeEnum < Types::BaseEnum + graphql_name "QuestionCheckType" + + values_from_enumerize(Question.check_type) + end +end diff --git a/app/graphql/enums/question_difficulty_enum.rb b/app/graphql/enums/question_difficulty_enum.rb new file mode 100644 index 0000000..96e9ff9 --- /dev/null +++ b/app/graphql/enums/question_difficulty_enum.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module Enums + class QuestionDifficultyEnum < Types::BaseEnum + graphql_name "QuestionDifficulty" + + values_from_enumerize(Question.difficulty) + end +end diff --git a/app/graphql/enums/question_status_enum.rb b/app/graphql/enums/question_status_enum.rb new file mode 100644 index 0000000..3adb985 --- /dev/null +++ b/app/graphql/enums/question_status_enum.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module Enums + class QuestionStatusEnum < Types::BaseEnum + graphql_name "QuestionStatus" + + values_from_enumerize(Question.status) + end +end diff --git a/app/graphql/inputs/date_range_input.rb b/app/graphql/inputs/date_range_input.rb new file mode 100644 index 0000000..60d7369 --- /dev/null +++ b/app/graphql/inputs/date_range_input.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Inputs + class DateRangeInput < Types::BaseInputObject + argument :start_at, GraphQL::Types::ISO8601Date, required: true + argument :end_at, GraphQL::Types::ISO8601Date, required: true + end +end diff --git a/app/graphql/inputs/question_where_input.rb b/app/graphql/inputs/question_where_input.rb new file mode 100644 index 0000000..99961d5 --- /dev/null +++ b/app/graphql/inputs/question_where_input.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Inputs + class QuestionWhereInput < Types::BaseInputObject + argument :check_type, [Enums::QuestionCheckTypeEnum], required: false + argument :status, [Enums::QuestionStatusEnum], required: false + argument :difficulty, [Enums::QuestionDifficultyEnum], required: false + argument :bloom_taxonomy, [Enums::QuestionBloomTaxonomyEnum], required: false + argument :authorship_year, [String], required: false + argument :subject_id, ID, required: false + argument :user_id, ID, required: false + argument :create_date, DateRangeInput, required: false + argument :unifeso_authorship, Boolean, required: false + end +end diff --git a/app/graphql/progress_test_schema.rb b/app/graphql/progress_test_schema.rb index 0179a68..e4caa11 100644 --- a/app/graphql/progress_test_schema.rb +++ b/app/graphql/progress_test_schema.rb @@ -16,22 +16,23 @@ class ProgressTestSchema < GraphQL::Schema # Union and Interface Resolution def self.resolve_type(abstract_type, obj, ctx) - # TODO: Implement this method - # to return the correct GraphQL object type for `obj` - raise(GraphQL::RequiredImplementationMissingError) + case obj + when Question + Types::QuestionType + else + raise("Unexpected object: #{obj}") + end end # Relay-style Object Identification: # Return a string UUID for `object` def self.id_from_object(object, type_definition, query_ctx) - # For example, use Rails' GlobalID library (https://github.com/rails/globalid): object.to_gid_param end # Given a string UUID, find the object def self.object_from_id(global_id, query_ctx) - # For example, use Rails' GlobalID library (https://github.com/rails/globalid): GlobalID.find(global_id) end end diff --git a/app/graphql/resolvers/questions_query_resolver.rb b/app/graphql/resolvers/questions_query_resolver.rb new file mode 100644 index 0000000..f495785 --- /dev/null +++ b/app/graphql/resolvers/questions_query_resolver.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true +module Resolvers + class QuestionsQueryResolver + def initialize(initial_scope, context:, where:) + @initial_scope = initial_scope + @context = context + @where = where.to_h + end + + def resolve + set_created_at_filter + + unifeso_authorship = @where.delete(:unifeso_authorship) + + scope = QuestionPolicy::Scope.new(@context[:current_user], @initial_scope).resolve + .where(@where) + .order(updated_at: :desc) + + return scope if unifeso_authorship.nil? + + unifeso_authorship ? scope.where(authorship: "UNIFESO") : scope.where.not(authorship: "UNIFESO") + end + + private + + def set_created_at_filter + create_date_range = @where.delete(:create_date) + + @where[:created_at] = create_date_range[:start_at]..create_date_range[:end_at] if create_date_range + end + end +end diff --git a/app/graphql/types/base_enum.rb b/app/graphql/types/base_enum.rb index b45a845..71b3973 100644 --- a/app/graphql/types/base_enum.rb +++ b/app/graphql/types/base_enum.rb @@ -1,4 +1,9 @@ module Types class BaseEnum < GraphQL::Schema::Enum + def self.values_from_enumerize(enum_values) + enum_values.values.each do |enum_value| + value enum_value.upcase, value: enum_value + end + end end end diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb index e884f8b..7600500 100644 --- a/app/graphql/types/query_type.rb +++ b/app/graphql/types/query_type.rb @@ -4,14 +4,12 @@ module Types include GraphQL::Types::Relay::HasNodeField include GraphQL::Types::Relay::HasNodesField - # Add root-level fields here. - # They will be entry points for queries on your schema. + field :questions, QuestionType.connection_type, null: false do + argument :where, Inputs::QuestionWhereInput, required: false + end - # TODO: remove me - field :test_field, String, null: false, - description: "An example field added by the generator" - def test_field - "Hello World!" + def questions(where: nil) + Resolvers::QuestionsQueryResolver.new(Question, context: context, where: where).resolve end end end diff --git a/app/graphql/types/question_alternative_type.rb b/app/graphql/types/question_alternative_type.rb new file mode 100644 index 0000000..df5d86a --- /dev/null +++ b/app/graphql/types/question_alternative_type.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module Types + module Questions + class Alternative < Types::BaseObject + graphql_name "QuestionAlternative" + + field :correct, Boolean, null: false + field :text, String, null: true + end + end +end diff --git a/app/graphql/types/question_type.rb b/app/graphql/types/question_type.rb new file mode 100644 index 0000000..37cf4f2 --- /dev/null +++ b/app/graphql/types/question_type.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Types + class QuestionType < Types::BaseObject + implements GraphQL::Types::Relay::Node + + graphql_name 'Question' + + global_id_field :id + field :user_id, Integer, null: false + field :subject_id, Integer + field :alternatives, GraphQL::Types::JSON, null: false + field :authorship, String + field :authorship_year, String + field :body, String + field :explanation, String + field :instruction, String + field :intention, String + field :references, String + field :support, String + field :bloom_taxonomy, Enums::QuestionBloomTaxonomyEnum + field :check_type, Enums::QuestionCheckTypeEnum + field :difficulty, Enums::QuestionDifficultyEnum + field :status, Enums::QuestionStatusEnum, null: false + field :created_at, GraphQL::Types::ISO8601DateTime, null: false + field :updated_at, GraphQL::Types::ISO8601DateTime, null: false + end +end diff --git a/app/policies/application_policy.rb b/app/policies/application_policy.rb index e000cba..eee630d 100644 --- a/app/policies/application_policy.rb +++ b/app/policies/application_policy.rb @@ -6,6 +6,11 @@ class ApplicationPolicy def initialize(user, record) @user = user @record = record + @roles = user.roles.map { |r| r.name.to_sym } + end + + def is?(role) + @roles.any?(role) end def index? diff --git a/app/policies/question_policy.rb b/app/policies/question_policy.rb new file mode 100644 index 0000000..7a42e7d --- /dev/null +++ b/app/policies/question_policy.rb @@ -0,0 +1,24 @@ +class QuestionPolicy < ApplicationPolicy + class Scope < Scope + def resolve + scope.all + end + end + + + def create? + user.roles.present? + end + + def update? + is?(:admin) || is?(:nde) || (is?(:teacher) && record.user_id == user.id) + end + + def destroy? + record.user_id == user.id && record.status != "registered" + end + + def finish? + (is?(:admin) || record.user_id == user.id) && record.status.to_sym == :approved + end +end