Skip to content

Commit

Permalink
feat: Private api endpoints for projects
Browse files Browse the repository at this point in the history
  • Loading branch information
martintomas committed Dec 31, 2024
1 parent e57da54 commit cebf5be
Show file tree
Hide file tree
Showing 19 changed files with 1,138 additions and 3 deletions.
100 changes: 100 additions & 0 deletions backend/app/controllers/api/v1/members/projects_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
module API
module V1
module Members
class ProjectsController < BaseController
include API::Pagination

skip_before_action :require_json!, only: %i[create update]

load_and_authorize_resource

def index
@projects = @projects.where member: current_member
pagy_object, @projects = pagy @projects, page: current_page, items: per_page unless params[:disable_pagination].to_s == "true"
render json: ProjectSerializer.new(
@projects,
include: included_relationships,
fields: sparse_fieldset,
links: pagy_object.present? ? pagination_links(:api_v1_projects_path, pagy_object) : nil,
meta: pagy_object.present? ? pagination_meta(pagy_object) : nil,
params: {current_member: current_member, current_ability: current_ability}
).serializable_hash
end

def show
render json: ProjectSerializer.new(
current_member.projects.find(params[:id]),
include: included_relationships,
fields: sparse_fieldset,
params: {current_member: current_member, current_ability: current_ability}
).serializable_hash
end

def create
@project = Project.new create_params
if @project.save
render json: ProjectSerializer.new(
@project,
include: included_relationships,
params: {current_member: current_member, current_ability: current_ability}
).serializable_hash
else
raise API::UnprocessableEntityError, @project.errors.full_messages.to_sentence
end
end

def update
if @project.update update_params
render json: ProjectSerializer.new(
@project,
include: included_relationships,
params: {current_member: current_member, current_ability: current_ability}
).serializable_hash
else
raise API::UnprocessableEntityError, @project.errors.full_messages.to_sentence
end
end

def destroy
@project.destroy!
head :ok
rescue ActiveRecord::DeleteRestrictionError
raise API::UnprocessableEntityError, @project.errors.full_messages.to_sentence
end

private

def create_params
update_params
end

def update_params
project_params.merge(
recipient_attributes: recipient_params.to_h
)
end

def project_params
{member_id: current_member.id}
end

def recipient_params
params.fetch(:project_params, {}).permit(
:name,
:description,
:logo,
:contact_first_name,
:contact_last_name,
:website,
:country_id,
:state_id,
:city,
:leadership_demographics_other,
:recipient_legal_status,
leadership_demographics: []
)
end
end
end
end
end
2 changes: 2 additions & 0 deletions backend/app/models/ability.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ def admin_rights
def member_rights
can %i[show update], Member, id: @member.id
can %i[show update], Funder, id: @member.funder_id
can :manage, Project, member_id: @member.id
can :manage, Recipient, project: {member_id: @member.id}
end

def default_rights
Expand Down
2 changes: 2 additions & 0 deletions backend/app/models/member.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ class Member < ApplicationRecord

belongs_to :funder

has_many :projects

validates_presence_of :first_name, :last_name

validates :password, length: {minimum: 12, message: :password_length}, allow_nil: true
Expand Down
3 changes: 2 additions & 1 deletion backend/app/models/project.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ class Project < ApplicationRecord
include PgSearch::Model

belongs_to :recipient
belongs_to :member, optional: true

has_many :investments, dependent: :destroy
has_many :investments, dependent: :restrict_with_exception
has_many :funders, -> { distinct }, through: :investments
has_many :investment_subgeographics, through: :investments
has_many :subgeographics, -> { distinct }, through: :investment_subgeographics
Expand Down
1 change: 1 addition & 0 deletions backend/config/routes/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@

namespace :members do
resource :funder, only: %i[show update]
resources :projects
end
end
end
5 changes: 5 additions & 0 deletions backend/db/migrate/20241231125414_add_member_to_projects.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddMemberToProjects < ActiveRecord::Migration[7.0]
def change
add_reference :projects, :member, null: true, foreign_key: true, type: :uuid
end
end
5 changes: 4 additions & 1 deletion backend/db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

62 changes: 62 additions & 0 deletions backend/spec/fixtures/snapshots/api/v1/members/create-project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{
"data": {
"id": "87af8776-4b68-4d0b-97c1-f02da0c77fe3",
"type": "project",
"attributes": {
"name": "New Project",
"description": "Description",
"website": "https://example.com",
"leadership_demographics": [
"black_or_african_american",
"other"
],
"leadership_demographics_other": "Something specific",
"areas": [

],
"demographics": [

],
"demographics_other": "",
"capital_types": [

],
"capital_type_other": "",
"recipient_legal_status": "for_profit",
"logo": {
"small": "http://localhost:4000/rails/active_storage/representations/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaEpJaWxqT0RrM1lXVmtaQzFqTmpJM0xUUm1PREF0T0ROaE5TMWxNV0poTkRjM05HSmtNek1HT2daRlZBPT0iLCJleHAiOm51bGwsInB1ciI6ImJsb2JfaWQifX0=--15fdb5e7fd7782bc1fed3d1e0d35b5bada98fd24/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdCem9MWm05eWJXRjBTU0lJYW5CbkJqb0dSVlE2QzNKbGMybDZaVWtpRERJd01IZ3lNREFHT3daVSIsImV4cCI6bnVsbCwicHVyIjoidmFyaWF0aW9uIn19--8673702c2d856505736727890dcac6a632977811/picture.jpg",
"medium": "http://localhost:4000/rails/active_storage/representations/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaEpJaWxqT0RrM1lXVmtaQzFqTmpJM0xUUm1PREF0T0ROaE5TMWxNV0poTkRjM05HSmtNek1HT2daRlZBPT0iLCJleHAiOm51bGwsInB1ciI6ImJsb2JfaWQifX0=--15fdb5e7fd7782bc1fed3d1e0d35b5bada98fd24/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdCem9MWm05eWJXRjBTU0lJYW5CbkJqb0dSVlE2QzNKbGMybDZaVWtpRERnd01IZzRNREFHT3daVSIsImV4cCI6bnVsbCwicHVyIjoidmFyaWF0aW9uIn19--e82a96c0eabd93d914de4ef37febd98c36e0e275/picture.jpg",
"original": "http://localhost:4000/rails/active_storage/blobs/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaEpJaWxqT0RrM1lXVmtaQzFqTmpJM0xUUm1PREF0T0ROaE5TMWxNV0poTkRjM05HSmtNek1HT2daRlZBPT0iLCJleHAiOm51bGwsInB1ciI6ImJsb2JfaWQifX0=--15fdb5e7fd7782bc1fed3d1e0d35b5bada98fd24/picture.jpg"
}
},
"relationships": {
"state": {
"data": {
"id": "fe085f5b-4eb4-4efb-bc95-2d91d2983108",
"type": "subgeographic"
}
},
"country": {
"data": {
"id": "308bb417-d74a-441f-8663-a9477536dd56",
"type": "subgeographic"
}
},
"subgeographics": {
"data": [

]
},
"subgeographic_ancestors": {
"data": [

]
},
"funders": {
"data": [

]
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"data": {
"id": "f70fc7dd-ee5d-4753-94b2-5fdb6f19eca0",
"type": "project",
"attributes": {
"name": "Otha Kemmer"
},
"relationships": {
"subgeographics": {
"data": [

]
},
"funders": {
"data": [

]
}
}
},
"included": [

]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"data": {
"id": "8054ba5f-f093-4f37-ac09-820c78538cae",
"type": "project",
"attributes": {
"name": "Otha Kemmer",
"description": "Enim repellat pariatur. Earum modi eos. Libero tempora exercitationem. Qui dolorem quo."
},
"relationships": {
}
}
}
62 changes: 62 additions & 0 deletions backend/spec/fixtures/snapshots/api/v1/members/get-project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{
"data": {
"id": "64e15dbc-529f-45a8-bc9c-5ce38c92bc2c",
"type": "project",
"attributes": {
"name": "Otha Kemmer",
"description": "Enim repellat pariatur. Earum modi eos. Libero tempora exercitationem. Qui dolorem quo.",
"website": "http://kutch-spencer.com/moises",
"leadership_demographics": [
"hispanic_or_latinx",
"other"
],
"leadership_demographics_other": "Enim repellat pariatur est.",
"areas": [

],
"demographics": [

],
"demographics_other": "",
"capital_types": [

],
"capital_type_other": "",
"recipient_legal_status": "for_profit",
"logo": {
"small": "http://localhost:4000/rails/active_storage/representations/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaEpJaWs1TW1FMk9EVmlOaTAwWkdJeExUUmtZemt0T0Rka01DMDJabVptTnpWaFpUSXlZVGtHT2daRlZBPT0iLCJleHAiOm51bGwsInB1ciI6ImJsb2JfaWQifX0=--cf2f156affcb2dd92def035e31a785d838c9b7ed/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdCem9MWm05eWJXRjBTU0lJYW5CbkJqb0dSVlE2QzNKbGMybDZaVWtpRERJd01IZ3lNREFHT3daVSIsImV4cCI6bnVsbCwicHVyIjoidmFyaWF0aW9uIn19--8673702c2d856505736727890dcac6a632977811/picture.jpg",
"medium": "http://localhost:4000/rails/active_storage/representations/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaEpJaWs1TW1FMk9EVmlOaTAwWkdJeExUUmtZemt0T0Rka01DMDJabVptTnpWaFpUSXlZVGtHT2daRlZBPT0iLCJleHAiOm51bGwsInB1ciI6ImJsb2JfaWQifX0=--cf2f156affcb2dd92def035e31a785d838c9b7ed/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdCem9MWm05eWJXRjBTU0lJYW5CbkJqb0dSVlE2QzNKbGMybDZaVWtpRERnd01IZzRNREFHT3daVSIsImV4cCI6bnVsbCwicHVyIjoidmFyaWF0aW9uIn19--e82a96c0eabd93d914de4ef37febd98c36e0e275/picture.jpg",
"original": "http://localhost:4000/rails/active_storage/blobs/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaEpJaWs1TW1FMk9EVmlOaTAwWkdJeExUUmtZemt0T0Rka01DMDJabVptTnpWaFpUSXlZVGtHT2daRlZBPT0iLCJleHAiOm51bGwsInB1ciI6ImJsb2JfaWQifX0=--cf2f156affcb2dd92def035e31a785d838c9b7ed/picture.jpg"
}
},
"relationships": {
"state": {
"data": {
"id": "4ba9e52c-2829-4a54-bab0-8cf2f8a46d09",
"type": "subgeographic"
}
},
"country": {
"data": {
"id": "d5071559-8dad-4abe-adfd-c9bf1ce0df07",
"type": "subgeographic"
}
},
"subgeographics": {
"data": [

]
},
"subgeographic_ancestors": {
"data": [

]
},
"funders": {
"data": [

]
}
}
}
}
Loading

0 comments on commit cebf5be

Please sign in to comment.