From 5ec6337d47cd13026778ec40be13e790fd30a6f4 Mon Sep 17 00:00:00 2001 From: cyrillefr Date: Thu, 6 Jun 2024 13:11:56 +0200 Subject: [PATCH 1/2] Add new GraphQL types for budget investments - added 2 new types - modified the models to get data through graphQL - modified the corresponding spec - also testing that hidden comments do not show up - modified comments specs bc now it returns comments on budget investments --- app/graphql/types/budget_investment_type.rb | 13 ++++ app/graphql/types/budget_type.rb | 19 ++++++ app/graphql/types/query_type.rb | 13 ++++ app/models/budget.rb | 1 + app/models/budget/investment.rb | 2 + app/models/comment.rb | 5 +- docs/en/features/graphql.md | 2 + docs/es/features/graphql.md | 2 + spec/lib/graphql_spec.rb | 72 ++++++++++++++++++++- spec/models/comment_spec.rb | 10 ++- 10 files changed, 133 insertions(+), 6 deletions(-) create mode 100644 app/graphql/types/budget_investment_type.rb create mode 100644 app/graphql/types/budget_type.rb diff --git a/app/graphql/types/budget_investment_type.rb b/app/graphql/types/budget_investment_type.rb new file mode 100644 index 000000000..00b6f32a0 --- /dev/null +++ b/app/graphql/types/budget_investment_type.rb @@ -0,0 +1,13 @@ +module Types + class BudgetInvestmentType < Types::BaseObject + field :id, ID, null: false + field :public_author, Types::UserType, null: true + field :price, GraphQL::Types::BigInt, null: true + field :feasibility, String, null: true + field :title, String, null: true + field :description, String, null: true + field :location, String, null: true + field :comments, Types::CommentType.connection_type, null: true + field :comments_count, Integer, null: true + end +end diff --git a/app/graphql/types/budget_type.rb b/app/graphql/types/budget_type.rb new file mode 100644 index 000000000..92ac2dfbe --- /dev/null +++ b/app/graphql/types/budget_type.rb @@ -0,0 +1,19 @@ +module Types + class BudgetType < Types::BaseObject + field :id, ID, null: false + field :name, String, null: true + field :phase, String, null: true + field :investments, Types::BudgetInvestmentType.connection_type, "Returns all investments", null: false + field :investment, Types::BudgetInvestmentType, null: false do + argument :id, ID, required: true, default_value: false + end + + def investments + Budget::Investment.public_for_api + end + + def investment(id:) + Budget::Investment.find(id) + end + end +end diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb index 9f854ed55..4e7697218 100644 --- a/app/graphql/types/query_type.rb +++ b/app/graphql/types/query_type.rb @@ -1,5 +1,10 @@ module Types class QueryType < Types::BaseObject + field :budgets, Types::BudgetType.connection_type, "Returns all budgets", null: false + field :budget, Types::BudgetType, "Returns budget for ID", null: false do + argument :id, ID, required: true, default_value: false + end + field :comments, Types::CommentType.connection_type, "Returns all comments", null: false field :comment, Types::CommentType, "Returns comment for ID", null: false do argument :id, ID, required: true, default_value: false @@ -47,6 +52,14 @@ module Types argument :id, ID, required: true, default_value: false end + def budgets + Budget.public_for_api + end + + def budget(id:) + Budget.find(id) + end + def comments Comment.public_for_api end diff --git a/app/models/budget.rb b/app/models/budget.rb index 894519282..90f9c3a4b 100644 --- a/app/models/budget.rb +++ b/app/models/budget.rb @@ -58,6 +58,7 @@ class Budget < ApplicationRecord scope :balloting, -> { where(phase: "balloting") } scope :reviewing_ballots, -> { where(phase: "reviewing_ballots") } scope :finished, -> { where(phase: "finished") } + scope :public_for_api, -> { published } class << self; undef :open; end scope :open, -> { where.not(phase: "finished") } diff --git a/app/models/budget/investment.rb b/app/models/budget/investment.rb index b6b2ae6be..871b3bc1f 100644 --- a/app/models/budget/investment.rb +++ b/app/models/budget/investment.rb @@ -13,6 +13,7 @@ class Budget include Mappable include Documentable include SDG::Relatable + include HasPublicAuthor acts_as_taggable_on :valuation_tags acts_as_votable @@ -111,6 +112,7 @@ class Budget end scope :for_render, -> { includes(:heading) } + scope :public_for_api, -> { where(budget: Budget.public_for_api) } def self.by_valuator(valuator_id) where(budget_valuator_assignments: { valuator_id: valuator_id }).joins(:valuator_assignments) diff --git a/app/models/comment.rb b/app/models/comment.rb index cad1bf8fe..3dda76443 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -38,7 +38,10 @@ class Comment < ApplicationRecord scope :sort_by_flags, -> { order(flags_count: :desc, updated_at: :desc) } scope :public_for_api, -> do not_valuations - .where(commentable: [Debate.public_for_api, Proposal.public_for_api, Poll.public_for_api]) + .where(commentable: [Debate.public_for_api, + Proposal.public_for_api, + Poll.public_for_api, + Budget::Investment.public_for_api]) end scope :sort_by_most_voted, -> { order(confidence_score: :desc, created_at: :desc) } diff --git a/docs/en/features/graphql.md b/docs/en/features/graphql.md index 8f278b59d..c6141eb95 100644 --- a/docs/en/features/graphql.md +++ b/docs/en/features/graphql.md @@ -130,6 +130,8 @@ The models are the following: | `User` | Users | | `Debate` | Debates | | `Proposal` | Proposals | +| `Budget` | Participatory budgets | +| `Budget::Investment` | Budget investments | | `Comment` | Comments on debates, proposals and other comments | | `Geozone` | Geozones (districts) | | `ProposalNotification` | Notifications related to proposals | diff --git a/docs/es/features/graphql.md b/docs/es/features/graphql.md index 1e4cfa708..0048fbb02 100644 --- a/docs/es/features/graphql.md +++ b/docs/es/features/graphql.md @@ -130,6 +130,8 @@ La lista de modelos es la siguiente: | `User` | Usuarios | | `Debate` | Debates | | `Proposal` | Propuestas | +| `Budget` | Presupuestos participativos | +| `Budget::Investment` | Proyectos de gasto | | `Comment` | Comentarios en debates, propuestas y otros comentarios | | `Geozone` | Geozonas (distritos) | | `ProposalNotification` | Notificaciones asociadas a propuestas | diff --git a/spec/lib/graphql_spec.rb b/spec/lib/graphql_spec.rb index 459547f16..c39939c8c 100644 --- a/spec/lib/graphql_spec.rb +++ b/spec/lib/graphql_spec.rb @@ -193,6 +193,17 @@ describe "Consul Schema" do end end + describe "Budgets" do + it "does not include unpublished budgets" do + create(:budget, :drafting, name: "Draft") + + response = execute("{ budgets { edges { node { name } } } }") + received_names = extract_fields(response, "budgets", "name") + + expect(received_names).to eq [] + end + end + describe "Debates" do it "does not include hidden debates" do create(:debate, title: "Visible") @@ -252,16 +263,17 @@ describe "Consul Schema" do end describe "Comments" do - it "only returns comments from proposals, debates and polls" do + it "only returns comments from proposals, debates, polls and Budget::Investment" do create(:comment, commentable: create(:proposal)) create(:comment, commentable: create(:debate)) create(:comment, commentable: create(:poll)) - build(:comment, commentable: create(:budget_investment)).save!(skip_validation: true) + create(:comment, commentable: create(:topic)) + create(:comment, commentable: create(:budget_investment)) response = execute("{ comments { edges { node { commentable_type } } } }") received_commentables = extract_fields(response, "comments", "commentable_type") - expect(received_commentables).to match_array ["Proposal", "Debate", "Poll"] + expect(received_commentables).to match_array ["Proposal", "Debate", "Poll", "Budget::Investment"] end it "displays comments of authors even if public activity is set to false" do @@ -336,6 +348,19 @@ describe "Consul Schema" do expect(received_comments).to match_array ["I can see the poll"] end + it "does not include comments from hidden investments" do + visible_investment = create(:budget_investment) + hidden_investment = create(:budget_investment, :hidden) + + create(:comment, commentable: visible_investment, body: "I can see the investment") + create(:comment, commentable: hidden_investment, body: "This investment is hidden!") + + response = execute("{ comments { edges { node { body } } } }") + received_comments = extract_fields(response, "comments", "body") + + expect(received_comments).to match_array ["I can see the investment"] + end + it "does not include comments of debates that are not public" do not_public_debate = create(:debate, :hidden) not_public_debate_comment = create(:comment, commentable: not_public_debate) @@ -369,6 +394,16 @@ describe "Consul Schema" do expect(received_comments).not_to include(not_public_poll_comment.body) end + it "does not include comments of investments that are not public" do + investment = create(:budget_investment, budget: create(:budget, :drafting)) + not_public_investment_comment = create(:comment, commentable: investment) + + response = execute("{ comments { edges { node { body } } } }") + received_comments = extract_fields(response, "comments", "body") + + expect(received_comments).not_to include(not_public_investment_comment.body) + end + it "only links public comments" do user = create(:administrator).user create(:comment, author: user, body: "Public") @@ -642,4 +677,35 @@ describe "Consul Schema" do expect(Time.zone.parse(received_timestamps.first)).to eq Time.zone.parse("2017-12-31 9:00:00") end end + + describe "Budget investment" do + it "does not include hidden comments" do + budget = create(:budget) + investment = create(:budget_investment, budget: budget) + + create(:comment, commentable: investment, body: "Visible") + create(:comment, :hidden, commentable: investment, body: "Hidden") + + query = <<~GRAPHQL + { + budget(id: #{budget.id}) { + investment(id: #{investment.id}) { + comments { + edges { + node { + body + } + } + } + } + } + } + GRAPHQL + + response = execute(query) + received_bodies = extract_fields(response, "budget.investment.comments", "body") + + expect(received_bodies).to eq ["Visible"] + end + end end diff --git a/spec/models/comment_spec.rb b/spec/models/comment_spec.rb index e5302665b..8fe0b6e30 100644 --- a/spec/models/comment_spec.rb +++ b/spec/models/comment_spec.rb @@ -175,8 +175,14 @@ describe Comment do expect(Comment.public_for_api).to be_empty end - it "does not return comments on elements which are not debates or proposals" do - create(:comment, commentable: create(:budget_investment)) + it "returns comments on budget investments" do + comment = create(:comment, commentable: create(:budget_investment)) + + expect(Comment.public_for_api).to eq [comment] + end + + it "does not return comments on elements which are not debates, proposals or budget investments" do + create(:comment, commentable: create(:topic)) expect(Comment.public_for_api).to be_empty end From 18323a36c3407659487f78a1e147da30981ce93c Mon Sep 17 00:00:00 2001 From: cyrillefr Date: Tue, 2 Jul 2024 15:47:42 +0200 Subject: [PATCH 2/2] Add new GraphQL type for milestones - added the milestone type to be displayed with investments - the corresponding spec --- app/graphql/types/budget_investment_type.rb | 1 + app/graphql/types/milestone_type.rb | 8 ++++++++ app/graphql/types/proposal_type.rb | 1 + app/graphql/types/query_type.rb | 13 +++++++++++++ app/models/milestone.rb | 3 +++ docs/en/features/graphql.md | 1 + docs/es/features/graphql.md | 1 + spec/lib/graphql_spec.rb | 10 ++++++++++ 8 files changed, 38 insertions(+) create mode 100644 app/graphql/types/milestone_type.rb diff --git a/app/graphql/types/budget_investment_type.rb b/app/graphql/types/budget_investment_type.rb index 00b6f32a0..15b1bd0d1 100644 --- a/app/graphql/types/budget_investment_type.rb +++ b/app/graphql/types/budget_investment_type.rb @@ -9,5 +9,6 @@ module Types field :location, String, null: true field :comments, Types::CommentType.connection_type, null: true field :comments_count, Integer, null: true + field :milestones, Types::MilestoneType.connection_type, null: true end end diff --git a/app/graphql/types/milestone_type.rb b/app/graphql/types/milestone_type.rb new file mode 100644 index 000000000..c7b9dd30f --- /dev/null +++ b/app/graphql/types/milestone_type.rb @@ -0,0 +1,8 @@ +module Types + class MilestoneType < Types::BaseObject + field :title, String, null: true + field :description, String, null: true + field :id, ID, null: false + field :publication_date, GraphQL::Types::ISO8601Date, null: true + end +end diff --git a/app/graphql/types/proposal_type.rb b/app/graphql/types/proposal_type.rb index 40b39cd01..7d9eb78a6 100644 --- a/app/graphql/types/proposal_type.rb +++ b/app/graphql/types/proposal_type.rb @@ -20,6 +20,7 @@ module Types field :title, String, null: true field :video_url, String, null: true field :votes_for, Types::VoteType.connection_type, null: true + field :milestones, Types::MilestoneType.connection_type, null: true def tags object.tags.public_for_api diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb index 4e7697218..cb823c8dd 100644 --- a/app/graphql/types/query_type.rb +++ b/app/graphql/types/query_type.rb @@ -20,6 +20,11 @@ module Types argument :id, ID, required: true, default_value: false end + field :milestones, Types::MilestoneType.connection_type, "Returns all milestones", null: false + field :milestone, Types::MilestoneType, "Returns milestone for ID", null: false do + argument :id, ID, required: true, default_value: false + end + field :proposals, Types::ProposalType.connection_type, "Returns all proposals", null: false field :proposal, Types::ProposalType, "Returns proposal for ID", null: false do argument :id, ID, required: true, default_value: false @@ -84,6 +89,14 @@ module Types Geozone.find(id) end + def milestones + Milestone.public_for_api + end + + def milestone(id:) + Milestone.find(id) + end + def proposals Proposal.public_for_api end diff --git a/app/models/milestone.rb b/app/models/milestone.rb index 47d2d2d03..b1794c652 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -16,6 +16,9 @@ class Milestone < ApplicationRecord scope :order_by_publication_date, -> { order(publication_date: :asc, created_at: :asc) } scope :published, -> { where(publication_date: ..Date.current.end_of_day) } scope :with_status, -> { where.not(status_id: nil) } + scope :public_for_api, -> do + where(milestoneable: [Proposal.public_for_api, Budget::Investment.public_for_api]) + end def self.title_max_length 80 diff --git a/docs/en/features/graphql.md b/docs/en/features/graphql.md index c6141eb95..c28d9deab 100644 --- a/docs/en/features/graphql.md +++ b/docs/en/features/graphql.md @@ -133,6 +133,7 @@ The models are the following: | `Budget` | Participatory budgets | | `Budget::Investment` | Budget investments | | `Comment` | Comments on debates, proposals and other comments | +| `Milestone` | Proposals, investments and processes milestones | | `Geozone` | Geozones (districts) | | `ProposalNotification` | Notifications related to proposals | | `Tag` | Tags on debates and proposals | diff --git a/docs/es/features/graphql.md b/docs/es/features/graphql.md index 0048fbb02..d4bb79a66 100644 --- a/docs/es/features/graphql.md +++ b/docs/es/features/graphql.md @@ -133,6 +133,7 @@ La lista de modelos es la siguiente: | `Budget` | Presupuestos participativos | | `Budget::Investment` | Proyectos de gasto | | `Comment` | Comentarios en debates, propuestas y otros comentarios | +| `Milestone` | Hitos en propuestas, proyectos de gasto y procesos | | `Geozone` | Geozonas (distritos) | | `ProposalNotification` | Notificaciones asociadas a propuestas | | `Tag` | Tags en debates y propuestas | diff --git a/spec/lib/graphql_spec.rb b/spec/lib/graphql_spec.rb index c39939c8c..f03f42ecb 100644 --- a/spec/lib/graphql_spec.rb +++ b/spec/lib/graphql_spec.rb @@ -678,6 +678,16 @@ describe "Consul Schema" do end end + describe "Milestone" do + it "formats publication date like in view" do + milestone = create(:milestone, publication_date: Time.zone.parse("2024-07-02 11:45:17")) + + response = execute("{ milestone(id: #{milestone.id}) { id publication_date } }") + received_publication_date = dig(response, "data.milestone.publication_date") + expect(received_publication_date).to eq "2024-07-02" + end + end + describe "Budget investment" do it "does not include hidden comments" do budget = create(:budget)