Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions app/controllers/api/v1/organizations_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
module Api
module V1
class OrganizationsController < BaseController
def index
organizations = current_user.organizations
render json: OrganizationSerializer.new(organizations).serialize, status: :ok
end

def create
organization = nil

ActiveRecord::Base.transaction do
organization = Organization.create!(organization_params)
organization.memberships.create!(user: current_user, role: :admin)
end

render json: OrganizationSerializer.new(organization).serialize, status: :created
end

private

def organization_params
params.expect(organization: [ :name ])
end
end
end
end
8 changes: 8 additions & 0 deletions app/models/membership.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class Membership < ApplicationRecord
belongs_to :user
belongs_to :organization

enum :role, { member: 0, admin: 1 }

validates :user_id, uniqueness: { scope: :organization_id }
end
15 changes: 15 additions & 0 deletions app/models/organization.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
class Organization < ApplicationRecord
has_many :memberships, dependent: :destroy
has_many :users, through: :memberships

before_validation :generate_slug, on: :create

validates :name, presence: true, length: { maximum: 100 }
validates :slug, presence: true, uniqueness: true

private

def generate_slug
self.slug = name.to_s.parameterize.presence
end
end
2 changes: 2 additions & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
class User < ApplicationRecord
has_secure_password
has_many :sessions, dependent: :destroy
has_many :memberships, dependent: :destroy
has_many :organizations, through: :memberships

EMAIL_FORMAT = URI::MailTo::EMAIL_REGEXP
MINIMUM_PASSWORD_LENGTH = 12
Expand Down
5 changes: 5 additions & 0 deletions app/serializers/organization_serializer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class OrganizationSerializer
include Alba::Resource

attributes :id, :name, :slug
end
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
resources :users, only: [ :show ]
resource :registration, only: [ :create ]
resource :session, only: [ :create, :destroy ]
resources :organizations, only: [ :index, :create ]
end
end

Expand Down
11 changes: 11 additions & 0 deletions db/migrate/20260807115335_create_organizations.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class CreateOrganizations < ActiveRecord::Migration[8.1]
def change
create_table :organizations do |t|
t.string :name, null: false
t.string :slug, null: false
t.timestamps
end

add_index :organizations, :slug, unique: true
end
end
12 changes: 12 additions & 0 deletions db/migrate/20260807115336_create_memberships.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class CreateMemberships < ActiveRecord::Migration[8.1]
def change
create_table :memberships do |t|
t.references :user, null: false, foreign_key: true
t.references :organization, null: false, foreign_key: true
t.integer :role, null: false, default: 0
t.timestamps
end

add_index :memberships, [ :user_id, :organization_id ], unique: true
end
end
23 changes: 22 additions & 1 deletion db/schema.rb

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

7 changes: 7 additions & 0 deletions spec/factories/memberships.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FactoryBot.define do
factory :membership do
user
organization
role { :member }
end
end
5 changes: 5 additions & 0 deletions spec/factories/organizations.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FactoryBot.define do
factory :organization do
sequence(:name) { |n| "Organization #{n}" }
end
end
29 changes: 29 additions & 0 deletions spec/requests/api/v1/organizations_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
require "rails_helper"

RSpec.describe "Api::V1::Organizations", type: :request do
let(:user) { create(:user) }

describe "POST /api/v1/organizations" do
it "creates the org and makes the creator an admin" do
post "/api/v1/organizations",
params: { organization: { name: "Acme Inc" } },
headers: auth_headers_for(user)

expect(response).to have_http_status(:created)
expect(json_body["slug"]).to eq("acme-inc")
expect(user.memberships.last).to be_admin
end
end

describe "GET /api/v1/organizations" do
it "returns only the organisations the user belongs to" do
mine = create(:organization)
create(:membership, user: user, organization: mine)
create(:organization)

get "/api/v1/organizations", headers: auth_headers_for(user)

expect(json_body.map { |o| o["id"] }).to eq([ mine.id ])
end
end
end