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