Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add logic of the user's invitation #3

Merged
merged 1 commit into from
Sep 17, 2024
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
41 changes: 41 additions & 0 deletions app/controllers/invites_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
class InvitesController < ApplicationController
before_action :authenticate_user!
before_action :set_workspace, only: [:new, :create]

Check failure on line 3 in app/controllers/invites_controller.rb

View workflow job for this annotation

GitHub Actions / lint

Layout/SpaceInsideArrayLiteralBrackets: Use space inside array brackets.

Check failure on line 3 in app/controllers/invites_controller.rb

View workflow job for this annotation

GitHub Actions / lint

Layout/SpaceInsideArrayLiteralBrackets: Use space inside array brackets.

def new
@invite = @workspace.invites.new
end

def create
@invite = Invite.new(invite_params)
result = InvitesService::Create.call(@workspace, @invite, current_user)

Check failure on line 12 in app/controllers/invites_controller.rb

View workflow job for this annotation

GitHub Actions / lint

Layout/TrailingWhitespace: Trailing whitespace detected.
if result.success?
redirect_to workspace_path(@workspace), notice: result[:payload][:text]
else

Check failure on line 15 in app/controllers/invites_controller.rb

View workflow job for this annotation

GitHub Actions / lint

Layout/TrailingWhitespace: Trailing whitespace detected.
redirect_to workspace_path(@workspace), notice: result[:error]
end
end

def accept
@invite = Invite.find_by(token: params[:token])

Check failure on line 22 in app/controllers/invites_controller.rb

View workflow job for this annotation

GitHub Actions / lint

Layout/TrailingWhitespace: Trailing whitespace detected.
result = InvitesService::Accept.call(@invite, current_user)
if result.success?
else
end
end

private

Check failure on line 30 in app/controllers/invites_controller.rb

View workflow job for this annotation

GitHub Actions / lint

Layout/TrailingWhitespace: Trailing whitespace detected.
def set_workspace
@workspace = current_user.workspaces.find_by(id: params[:workspace_id])
unless @workspace
redirect_to workspaces_path, alert: 'Workspace not found or not accessible.'
end
end

def invite_params
params.require(:invite).permit(:email, :invite_type)
end
end
6 changes: 5 additions & 1 deletion app/controllers/user_workspaces_controller.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
class UserWorkspacesController < ApplicationController
before_action :authenticate_user!
before_action :set_workspace
before_action :set_user_workspace, only: [:edit, :update]

Expand All @@ -24,7 +25,10 @@ def set_user_workspace
end

def set_workspace
@workspace = Workspace.find(params[:workspace_id])
@workspace = current_user.workspaces.find_by(id: params[:id])
unless @workspace
redirect_to workspaces_path, alert: 'Workspace not found or not accessible.'
end
end

def user_workspace_params
Expand Down
7 changes: 4 additions & 3 deletions app/controllers/workspaces_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ def index

def show
@user_workspace = @workspace.user_workspaces.find_by(user: current_user)

@invite = @workspace.invites.new
unless @user_workspace
redirect_to workspaces_path, alert: 'You are not associated with this workspace.'
end
Expand All @@ -22,8 +22,9 @@ def create
@workspace = Workspace.new(workspace_params)
@workspace.user = current_user

if @workspace.valid?
WorkspacesUserService::Create.new(@workspace).call(current_user)
result = WorkspacesUserService::Create.call(@workspace, current_user)

if result.success?
redirect_to workspaces_path, notice: 'Workspace was successfully created.'
else
render :new
Expand Down
2 changes: 2 additions & 0 deletions app/helpers/invites_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
module InvitesHelper
end
8 changes: 8 additions & 0 deletions app/mailers/invite_mailer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class InviteMailer < ApplicationMailer
def invitation_email(invite, workspace)
@invite = invite
@workspace = workspace

mail(to: @invite.email, subject: "You were invited to #{@workspace.name} workspace")
end
end
20 changes: 20 additions & 0 deletions app/models/invite.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
class Invite < ApplicationRecord
belongs_to :workspace
belongs_to :user

before_create :generate_token

validates :invite_type, inclusion: { in: %w[public private], message: "%{value} is not a valid type" }
validates :status, inclusion: { in: %w[unconfirmed confirmed], message: "%{value} is not a valid type" }
validates :email, presence: true, if: :private_invite?

private

def generate_token
self.token = SecureRandom.hex(10)
end

def private_invite?
invite_type == 'private'
end
end
1 change: 1 addition & 0 deletions app/models/workspace.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
class Workspace < ApplicationRecord
has_many :user_workspaces, dependent: :destroy
has_many :users, through: :user_workspaces
has_many :invites
belongs_to :user

has_one_attached :picture
Expand Down
9 changes: 9 additions & 0 deletions app/policies/invite_policy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class InvitePolicy < ApplicationPolicy
def confirmed?
@record[:status] == 'confirmed'
end

def private?
@record[:invite_type] == 'private'
end
end
3 changes: 2 additions & 1 deletion app/services/application_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ def success(payload = nil)
def failure(exception, options = {})
raise exception if @propagate

ErrorService.error(exception, options)
# TODO
# ErrorService.error(exception, options)
Response.new(false, nil, exception)
end
end
44 changes: 44 additions & 0 deletions app/services/invites_service/accept.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
module InvitesService
class Accept < ApplicationService
def call(invite, current_user)
failure('Invite does not exists') if invite.nil?
@invite = invite
@user = current_user
@invite_policy = InvitePolicy.new(current_user, @invite)

if @invite_policy.private?
accept_private_invite
else
accept_public_invite
end

end

private

def accept_private_invite
return failure({ text: 'Invite already confirmed' }) if @invite_policy.confirmed?
@invite.update(status: 'confirmed')

assign_user_to_workspace
end

def accept_public_invite
if @invite.workspace.users.exists?(@user.id)
return failure({ text: 'User is already in the workspace' })
end

assign_user_to_workspace
end

def assign_user_to_workspace
result = WorkspacesUserService::Create.call(@invite.workspace, @user)

if result.success?
success({ text: 'User successfully joined the workspace' })
else
success({ text: 'User cant join the workspace. Try again later' })
end
end
end
end
21 changes: 21 additions & 0 deletions app/services/invites_service/create.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module InvitesService
class Create < ApplicationService
include Rails.application.routes.url_helpers

def call(workspace, invite, current_user)
@invite = invite
@invite.workspace = workspace
@invite.user = current_user

if InvitePolicy.new(current_user, @invite).private?
@invite.save
InviteMailer.invitation_email(@invite, workspace).deliver_now
success({ text: 'Invite was sent to the user' })
else
@invite.save
link = accept_invite_url(@invite.token, host: Rails.application.config.default_url_options[:host], port: Rails.application.config.default_url_options[:port] )
success({ text: "Share your link: #{link}" })
end
end
end
end
7 changes: 2 additions & 5 deletions app/services/workspaces_user_service/create.rb
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
module WorkspacesUserService
class Create < ApplicationService
def initialize(workspace)
def call(workspace, current_user)
@workspace = workspace
end

def call(current_user)

if @workspace.save
UserWorkspace.create!(user: current_user, workspace: @workspace, profile_name: current_user.name, profile_status: 'away')
success(@workspace)
else
failure("Workspace could not be saved.")
end
end

end
end
9 changes: 9 additions & 0 deletions app/views/invite_mailer/invitation_email.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<body>
<h1>Invitation to workspace</h1>
<p>Dear <%= @invite.email %>,</p>
<p>You were invited to "<%= @workspace.name %>".</p>
<p>Follow the link to accept the invitation: <%= link_to 'Link', accept_invite_url(token: @invite.token) %>.</p>
</body>
</html>
15 changes: 15 additions & 0 deletions app/views/invites/_form.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<%= form_for(model: [@workspace, @invite]) do |f| %>
<div class="field">
<%= f.label :invite_type %>
<%= f.select :invite_type, ['private', 'public'] %>
</div>

<div class="field">
<%= f.label :email %>
<%= f.text_field :email %>
</div>

<div class="actions">
<%= f.submit "Create Invite" %>
</div>
<% end %>
Empty file.
18 changes: 18 additions & 0 deletions app/views/workspaces/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,21 @@
<p>Profile Name: <%= @user_workspace.profile_name %></p>
<p>Status: <%= @user_workspace.profile_status %></p>
<p>Description: <%= @user_workspace.profile_description %></p>

<h2>Invite a User</h2>

<%= form_with(model: [@workspace, @invite], local: true) do |form| %>
<div class="field">
<%= form.label :email, "User Email" %>
<%= form.email_field :email %>
</div>

<div class="field">
<%= form.label :invite_type, "Invite Type" %>
<%= form.select :invite_type, options_for_select([['Private', 'private'], ['Public', 'public']]) %>
</div>

<div class="actions">
<%= form.submit "Send Invite" %>
</div>
<% end %>
1 change: 1 addition & 0 deletions config/environments/development.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
# config.generators.apply_rubocop_autocorrect_after_generate!

config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
config.default_url_options = { host: 'localhost', port: 3000 }
config.action_mailer.delivery_method = :smtp
config.action_mailer.default charset: 'utf-8'
config.action_mailer.smtp_settings = {
Expand Down
3 changes: 3 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
devise_for :users
resources :workspaces do
resource :user_workspace, only: [:edit, :update]
resource :invites, only: [:create]
end

get 'invites/accept/:token', to: 'invites#accept', as: 'accept_invite'

# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html

# Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.
Expand Down
14 changes: 14 additions & 0 deletions db/migrate/20240916101407_create_invites.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
class CreateInvites < ActiveRecord::Migration[7.2]
def change
create_table :invites do |t|
t.string :email, null: false
t.string :token, null: false
t.references :user, foreign_key: true
t.references :workspace, foreign_key: true

t.timestamps
end

add_index :invites, :token, unique: true
end
end
5 changes: 5 additions & 0 deletions db/migrate/20240916103731_add_type_to_invites.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddTypeToInvites < ActiveRecord::Migration[7.2]
def change
add_column :invites, :type, :string, default: 'public'
end
end
5 changes: 5 additions & 0 deletions db/migrate/20240917064343_rename_type_in_invites.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class RenameTypeInInvites < ActiveRecord::Migration[7.2]
def change
rename_column :invites, :type, :invite_type
end
end
5 changes: 5 additions & 0 deletions db/migrate/20240917070708_add_status_to_invites.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddStatusToInvites < ActiveRecord::Migration[7.2]
def change
add_column :invites, :status, :string, default: 'unconfirmed'
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class ChangeEmailToBeNullableInInvites < ActiveRecord::Migration[7.2]
def change
change_column_null :invites, :email, true
end
end
18 changes: 17 additions & 1 deletion db/schema.rb

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

Loading