amazon-chime-sdk-rails
amazon-chime-sdk-rails brings server-side implementation of Amazon Chime SDK to your Ruby on Rails application. Amazon Chime SDK provides client-side implementation to build real-time communications for your application, and amazon-chime-sdk-rails enables you to easily add server-side implementation to your Rails application.
amazon-chime-sdk-rails supports both of Rails API Application and Rails Application with Action View. The gem provides following functions:
- Meeting Coordinator - Wrapper client module of AWS SDK for Ruby, which simulates AWS SDK for JavaScript to communicate with Amazon Chime SDK client implementation by JSON format.
- Controller Templates - Mixin module implementation for meetings and attendees controllers.
- Rails Generators
- Controller Generator - Generator to create customizable meetings and attendees controllers in your Rails application.
- View Generator - Generator to create customizable meetings views for your Rails application with Action View.
- Single Javascript Generator - Generator to bundle Amazon Chime SDK into single .js file and put it into Asset Pipeline for your Rails application with Action View.
Getting Started
Installation
Add amazon-chime-sdk-rails to your app’s Gemfile:
gem 'amazon-chime-sdk-rails'
Then, in your project directory:
$ bundle install
$ rails g chime_sdk:install
The install generator will generate an initializer which describes all configuration options of amazon-chime-sdk-rails.
Set up AWS credentials
You need to set up AWS credentials or IAM role for amazon-chime-sdk-rails in your Rails app. See Configuring the AWS SDK for Ruby for more details.
amazon-chime-sdk-rails requires following IAM permissions:
- chime:TagResource
- chime:UntagResource
- chime:CreateMeeting
- chime:GetMeeting
- chime:ListMeetings (if necessary)
- chime:DeleteMeeting (if necessary)
- chime:CreateAttendee
- chime:GetAttendee
- chime:ListAttendees (if necessary)
- chime:DeleteAttendee (if necessary)
See Actions defined by Amazon Chime for more details.
A: Develop your Rails API Application
Let's start to building simple Rails API application providing real-time communications in a private room. For example, create Rails API application using Devise Token Auth for user authentication.
Prepare Rails API application
At first, create new Rails application:
$ rails new chime_api_app --api
$ cd chime_api_app
Add gems to your Gemfile:
# Gemfile
gem 'devise_token_auth'
gem 'amazon-chime-sdk-rails'
Then, install devise_token_auth:
$ bundle install
$ rails g devise:install
$ rails g devise_token_auth:install User auth
Update your application_controller.rb
like this:
# app/controllers/application_controller.rb
class ApplicationController < ActionController::API
include ActionController::Helpers
include DeviseTokenAuth::Concerns::SetUserByToken
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:name])
end
end
Update your user.rb
to remove unnecessary options like this:
# app/models/user.rb
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable
include DeviseTokenAuth::Concerns::User
end
Update devise_token_auth configuration in devise_token_auth.rb
to keep authorization headers:
# config/initializers/devise_token_auth.rb
DeviseTokenAuth.setup do |config|
# Uncomment and update
config.change_headers_on_each_request = false
end
Create private room functions
Create models and controllers by generator:
$ rails g model room name:string
$ rails g scaffold_controller api/rooms name:string --model-name=room
$ rails g model entry room:references user:references
$ rails g scaffold_controller api/entries room:references user:references --model-name=entry
$ rake db:migrate
Update your room.rb
like this:
# app/models/room.rb
class Room < ApplicationRecord
has_many :entries, dependent: :destroy
has_many :members, through: :entries, source: :user
def member?(user)
members.include?(user)
end
def as_json(options = {})
super options.merge(:methods => [:members])
end
end
Add uniqueness validation to your entry.rb
like this:
# app/models/entry.rb
class Entry < ApplicationRecord
belongs_to :room
belongs_to :user
# Add uniqueness validation
validates :user, uniqueness: { scope: :room }
end
Remove location header from your rooms_controller.rb
and entries_controller.rb
like this:
# app/controllers/api/rooms_controller.rb
# POST /rooms
def create
@room = Room.new(room_params)
if @room.save
render json: @room, status: :created # Remove location header
else
render json: @room.errors, status: :unprocessable_entity
end
end
# app/controllers/api/entries_controller.rb
# POST /entries
def create
@entry = Entry.new(entry_params)
if @entry.save
render json: @entry, status: :created # Remove location header
else
render json: @entry.errors, status: :unprocessable_entity
end
end
Develop meeting functions with amazon-chime-sdk-rails
Install amazon-chime-sdk-rails and generates your controllers:
$ rails g chime_sdk:install
$ rails g chime_sdk:controllers -r room -n api
Add and uncomment several functions in generated meetings_controller.rb
and meeting_attendees_controller.rb
for your app configurations:
# app/controllers/api/meetings_controller.rb
class Api::MeetingsController < ApplicationController
before_action :authenticate_api_user!
include DeviseTokenAuth::Concerns::SetUserByToken
before_action :set_room
before_action :check_membership
include ChimeSdk::Controller::Meetings::Mixin
private
# Add
def set_room
@room = Room.find(params[:room_id])
end
# Add
def check_membership
unless @room.member?(current_api_user)
message = 'Unauthorized: you are not a member of this private room.'
render json: { room: @room, notice: message }, status: :forbidden
end
end
# Uncomment
def meeting_request_id
"PrivateRoom-#{@room.id}"
end
# Uncomment and update
def attendee_request_id
"User-#{current_api_user.id}"
end
# Uncomment
def application_meeting_metadata(meeting)
{
"MeetingType": "PrivateRoom",
"Room": @room
}
end
# Uncomment
def application_attendee_metadata(attendee)
user_id = attendee[:Attendee][:ExternalUserId].split('-')[3]
{
"AttendeeType": "User",
"User": User.find_by_id(user_id)
}
end
end
# app/controllers/api/meeting_attendees_controller.rb
class Api::MeetingAttendeesController < ApplicationController
before_action :authenticate_api_user!
include DeviseTokenAuth::Concerns::SetUserByToken
before_action :set_room
before_action :check_membership
include ChimeSdk::Controller::Attendees::Mixin
private
# Add
def set_room
@room = Room.find(params[:room_id])
end
# Add
def check_membership
unless @room.member?(current_api_user)
message = 'Unauthorized: you are not a member of this private room.'
render json: { room: @room, notice: message }, status: :forbidden
end
end
# Uncomment and update
def attendee_request_id
"User-#{current_api_user.id}"
end
# Uncomment
def application_attendee_metadata(attendee)
user_id = attendee[:Attendee][:ExternalUserId].split('-')[3]
{
"AttendeeType": "User",
"User": User.find_by_id(user_id)
}
end
end
Then, update your routes.rb
like this:
# config/routes.rb
Rails.application.routes.draw do
namespace :api do
scope :"v1" do
mount_devise_token_auth_for 'User', at: 'auth'
resources :rooms do
resources :entries, only: [:create, :destroy]
resources :meetings, defaults: { format: :json }, only: [:index, :show, :create, :destroy] do
resources :meeting_attendees, as: :attendees, path: :attendees, only: [:index, :show, :create, :destroy]
end
end
end
end
end
Note that you need to set up AWS credentials or IAM role for amazon-chime-sdk-rails. See Set up AWS credentials for more details.
Finally, start rails server:
$ rails server
Now ready to take off!
Get meeting configurations through Rails API
Created Rails API works like this:
# Sign up users
$ curl localhost:3000/api/v1/auth -X POST -H "content-type:application/json" -d '{"email":"[email protected]", "password":"password", "password_confirmation":"password", "name":"ichiro"}'
{"status":"success","data":{"id":1,"provider":"email","uid":"[email protected]","allow_password_change":false,"name":"ichiro","nickname":null,"image":null,"email":"[email protected]","created_at":"2020-10-16T11:14:48.731Z","updated_at":"2020-10-16T11:14:48.827Z"}}
$ curl localhost:3000/api/v1/auth -X POST -H "content-type:application/json" -d '{"email":"[email protected]", "password":"password", "password_confirmation":"password", "name":"stephen"}'
{"status":"success","data":{"id":2,"provider":"email","uid":"[email protected]","allow_password_change":false,"name":"stephen","nickname":null,"image":null,"email":"[email protected]","created_at":"2020-10-16T11:15:33.226Z","updated_at":"2020-10-16T11:15:33.314Z"}}
# Create private room
$ curl localhost:3000/api/v1/rooms -X POST -H "content-type:application/json" -d '{"room":{"name":"PrivateRoom-1"}}'
{"id":1,"name":"PrivateRoom-1","created_at":"2020-10-16T11:15:56.223Z","updated_at":"2020-10-16T11:15:56.223Z","members":[]}
# You cannot create meeting yet because the user is not signed in
$ curl localhost:3000/api/v1/rooms/3/meetings -X POST -H "content-type:application/json"
{"errors":["You need to sign in or sign up before continuing."]}
# Sign in as ichiro
$ curl localhost:3000/api/v1/auth/sign_in -X POST -H "content-type:application/json" -D auth_headers.txt -d '{"email":"[email protected]", "password":"password"}'
{"data":{"id":1,"email":"[email protected]","provider":"email","uid":"[email protected]","allow_password_change":false,"name":"ichiro","nickname":null,"image":null}}
$ _ACCESS_TOKEN=$(cat auth_headers.txt | grep access-token | rev | cut -c 2- | rev)
$ _CLIENT=$(cat auth_headers.txt | grep client | rev | cut -c 2- | rev)
$ _UID=$(cat auth_headers.txt | grep uid | rev | cut -c 2- | rev)
# You cannot create meeting yet because the user is not a member of the private room
$ curl localhost:3000/api/v1/rooms/1/meetings -X POST -H "content-type:application/json" -H "${_ACCESS_TOKEN}" -H "${_CLIENT}" -H "${_UID}"
{"room":{"id":1,"name":"PrivateRoom-1","created_at":"2020-10-16T11:15:56.223Z","updated_at":"2020-10-16T11:15:56.223Z","members":[]},"notice":"Unauthorized: you are not a member of this private room."}
# Add users to the private room
$ curl localhost:3000/api/v1/rooms/1/entries -X POST -H "content-type:application/json" -d '{"entry":{"room_id":1,"user_id":1}}'
{"id":1,"room_id":1,"user_id":1,"created_at":"2020-10-16T11:18:22.839Z","updated_at":"2020-10-16T11:18:22.839Z"}
$ curl localhost:3000/api/v1/rooms/1/entries -X POST -H "content-type:application/json" -d '{"entry":{"room_id":1,"user_id":2}}'
{"id":2,"room_id":1,"user_id":2,"created_at":"2020-10-16T11:18:41.116Z","updated_at":"2020-10-16T11:18:41.116Z"}
# Now you can create meeting as a member of the private room
$ curl localhost:3000/api/v1/rooms/1/meetings -X POST -H "content-type:application/json" -H "${_ACCESS_TOKEN}" -H "${_CLIENT}" -H "${_UID}" | jq .
{
"Meeting": {
"MeetingId": "2f550432-579c-4058-bbb9-be8a01d3beea",
"ExternalMeetingId": "ChimeSdkRailsApp-development-PrivateRoom-1",
"MediaPlacement": {
"AudioHostUrl": "d3175d855e633b72aedb275a0dd6504f.k.m2.ue1.app.chime.aws:3478",
"AudioFallbackUrl": "wss://haxrp.m2.ue1.app.chime.aws:443/calls/2f550432-579c-4058-bbb9-be8a01d3beea",
"ScreenDataUrl": "wss://bitpw.m2.ue1.app.chime.aws:443/v2/screen/2f550432-579c-4058-bbb9-be8a01d3beea",
"ScreenSharingUrl": "wss://bitpw.m2.ue1.app.chime.aws:443/v2/screen/2f550432-579c-4058-bbb9-be8a01d3beea",
"ScreenViewingUrl": "wss://bitpw.m2.ue1.app.chime.aws:443/ws/connect?passcode=null&viewer_uuid=null&X-BitHub-Call-Id=2f550432-579c-4058-bbb9-be8a01d3beea",
"SignalingUrl": "wss://signal.m2.ue1.app.chime.aws/control/2f550432-579c-4058-bbb9-be8a01d3beea",
"TurnControlUrl": "https://ccp.cp.ue1.app.chime.aws/v2/turn_sessions"
},
"MediaRegion": "us-east-1",
"ApplicationMetadata": {
"MeetingType": "PrivateRoom",
"Room": {
"id": 1,
"name": "PrivateRoom-1",
"created_at": "2020-10-16T11:15:56.223Z",
"updated_at": "2020-10-16T11:15:56.223Z",
"members": [
{
"id": 1,
"provider": "email",
"uid": "[email protected]",
"allow_password_change": false,
"name": "ichiro",
"nickname": null,
"image": null,
"email": "[email protected]",
"created_at": "2020-10-16T11:14:48.731Z",
"updated_at": "2020-10-16T11:16:56.927Z"
},
{
"id": 2,
"provider": "email",
"uid": "[email protected]",
"allow_password_change": false,
"name": "stephen",
"nickname": null,
"image": null,
"email": "[email protected]",
"created_at": "2020-10-16T11:15:33.226Z",
"updated_at": "2020-10-16T11:15:33.314Z"
}
]
}
}
},
"Attendee": {
"ExternalUserId": "ChimeSdkRailsApp-development-User-1",
"AttendeeId": "b581c46d-661f-92bb-d80e-f4b157d95fe9",
"JoinToken": "YjU4MWM0NmQtNjYxZi05MmJiLWQ4MGUtZjRiMTU3ZDk1ZmU5OjgyZmM2NTMxLTIwMjctNGMxMS04OTE0LTQwZjkxNmJmNjM3MQ",
"ApplicationMetadata": {
"AttendeeType": "User",
"User": {
"id": 1,
"provider": "email",
"uid": "[email protected]",
"allow_password_change": false,
"name": "ichiro",
"nickname": null,
"image": null,
"email": "[email protected]",
"created_at": "2020-10-16T11:14:48.731Z",
"updated_at": "2020-10-16T11:16:56.927Z"
}
}
}
}
# Get attendee data from created meeting ID
$ MEETING_ID=$(curl localhost:3000/api/v1/rooms/1/meetings -X POST -H "content-type:application/json" -H "${_ACCESS_TOKEN}" -H "${_CLIENT}" -H "${_UID}" | jq -r .Meeting.MeetingId)
$ curl localhost:3000/api/v1/rooms/1/meetings/${MEETING_ID}/attendees -H "content-type:application/json" -H "${_ACCESS_TOKEN}" -H "${_CLIENT}" -H "${_UID}" | jq .
{
"attendees": [
{
"Attendee": {
"ExternalUserId": "ChimeSdkRailsApp-development-User-1",
"AttendeeId": "b581c46d-661f-92bb-d80e-f4b157d95fe9",
"JoinToken": "YjU4MWM0NmQtNjYxZi05MmJiLWQ4MGUtZjRiMTU3ZDk1ZmU5OjgyZmM2NTMxLTIwMjctNGMxMS04OTE0LTQwZjkxNmJmNjM3MQ",
"ApplicationMetadata": {
"AttendeeType": "User",
"User": {
"id": 1,
"provider": "email",
"uid": "[email protected]",
"allow_password_change": false,
"name": "ichiro",
"nickname": null,
"image": null,
"email": "[email protected]",
"created_at": "2020-10-16T11:14:48.731Z",
"updated_at": "2020-10-16T11:16:56.927Z"
}
}
}
}
]
}
$ ATTENDEE_ID=$(curl localhost:3000/api/v1/rooms/1/meetings/${MEETING_ID}/attendees -X GET -H "content-type:application/json" -H "${_ACCESS_TOKEN}" -H "${_CLIENT}" -H "${_UID}" | jq -r .attendees[0].Attendee.AttendeeId)
$ curl localhost:3000/api/v1/rooms/1/meetings/${MEETING_ID}/attendees/${ATTENDEE_ID} -H "content-type:application/json" -H "${_ACCESS_TOKEN}" -H "${_CLIENT}" -H "${_UID}" | jq .
{
"Attendee": {
"ExternalUserId": "ChimeSdkRailsApp-development-User-1",
"AttendeeId": "b581c46d-661f-92bb-d80e-f4b157d95fe9",
"JoinToken": "YjU4MWM0NmQtNjYxZi05MmJiLWQ4MGUtZjRiMTU3ZDk1ZmU5OjgyZmM2NTMxLTIwMjctNGMxMS04OTE0LTQwZjkxNmJmNjM3MQ",
"ApplicationMetadata": {
"AttendeeType": "User",
"User": {
"id": 1,
"provider": "email",
"uid": "[email protected]",
"allow_password_change": false,
"name": "ichiro",
"nickname": null,
"image": null,
"email": "[email protected]",
"created_at": "2020-10-16T11:14:48.731Z",
"updated_at": "2020-10-16T11:16:56.927Z"
}
}
}
}
# Sign in as stephen
$ curl localhost:3000/api/v1/auth/sign_in -X POST -H "content-type:application/json" -D auth_headers.txt -d '{"email":"[email protected]", "password":"password"}'
{"data":{"id":2,"email":"[email protected]","provider":"email","uid":"[email protected]","allow_password_change":false,"name":"stephen","nickname":null,"image":null}}
$ _ACCESS_TOKEN=$(cat auth_headers.txt | grep access-token | rev | cut -c 2- | rev)
$ _CLIENT=$(cat auth_headers.txt | grep client | rev | cut -c 2- | rev)
$ _UID=$(cat auth_headers.txt | grep uid | rev | cut -c 2- | rev)
# Confirm attending same meeting in the private room as different attendee
$ curl localhost:3000/api/v1/rooms/1/meetings -X POST -H "content-type:application/json" -H "${_ACCESS_TOKEN}" -H "${_CLIENT}" -H "${_UID}" | jq .
{
"Meeting": {
"MeetingId": "2f550432-579c-4058-bbb9-be8a01d3beea",
"ExternalMeetingId": "ChimeSdkRailsApp-development-PrivateRoom-1",
"MediaPlacement": {
"AudioHostUrl": "d3175d855e633b72aedb275a0dd6504f.k.m2.ue1.app.chime.aws:3478",
"AudioFallbackUrl": "wss://haxrp.m2.ue1.app.chime.aws:443/calls/2f550432-579c-4058-bbb9-be8a01d3beea",
"ScreenDataUrl": "wss://bitpw.m2.ue1.app.chime.aws:443/v2/screen/2f550432-579c-4058-bbb9-be8a01d3beea",
"ScreenSharingUrl": "wss://bitpw.m2.ue1.app.chime.aws:443/v2/screen/2f550432-579c-4058-bbb9-be8a01d3beea",
"ScreenViewingUrl": "wss://bitpw.m2.ue1.app.chime.aws:443/ws/connect?passcode=null&viewer_uuid=null&X-BitHub-Call-Id=2f550432-579c-4058-bbb9-be8a01d3beea",
"SignalingUrl": "wss://signal.m2.ue1.app.chime.aws/control/2f550432-579c-4058-bbb9-be8a01d3beea",
"TurnControlUrl": "https://ccp.cp.ue1.app.chime.aws/v2/turn_sessions"
},
"MediaRegion": "us-east-1",
"ApplicationMetadata": {
"MeetingType": "PrivateRoom",
"Room": {
"id": 1,
"name": "PrivateRoom-1",
"created_at": "2020-10-16T11:15:56.223Z",
"updated_at": "2020-10-16T11:15:56.223Z",
"members": [
{
"id": 1,
"provider": "email",
"uid": "[email protected]",
"allow_password_change": false,
"name": "ichiro",
"nickname": null,
"image": null,
"email": "[email protected]",
"created_at": "2020-10-16T11:14:48.731Z",
"updated_at": "2020-10-16T11:16:56.927Z"
},
{
"id": 2,
"provider": "email",
"uid": "[email protected]",
"allow_password_change": false,
"name": "stephen",
"nickname": null,
"image": null,
"email": "[email protected]",
"created_at": "2020-10-16T11:15:33.226Z",
"updated_at": "2020-10-16T11:21:46.011Z"
}
]
}
}
},
"Attendee": {
"ExternalUserId": "ChimeSdkRailsApp-development-User-2",
"AttendeeId": "986886fc-dcbc-1d44-4708-917ab23117de",
"JoinToken": "OTg2ODg2ZmMtZGNiYy0xZDQ0LTQ3MDgtOTE3YWIyMzExN2RlOjNjNjAzM2E5LWFlNGUtNGVmZi1iNjZjLWMwY2M1YjU3OTE4OA",
"ApplicationMetadata": {
"AttendeeType": "User",
"User": {
"id": 2,
"provider": "email",
"uid": "[email protected]",
"allow_password_change": false,
"name": "stephen",
"nickname": null,
"image": null,
"email": "[email protected]",
"created_at": "2020-10-16T11:15:33.226Z",
"updated_at": "2020-10-16T11:21:46.011Z"
}
}
}
}
$ MEETING_ID_2=$(curl localhost:3000/api/v1/rooms/1/meetings -X POST -H "content-type:application/json" -H "${_ACCESS_TOKEN}" -H "${_CLIENT}" -H "${_UID}" | jq -r .Meeting.MeetingId)
$ echo ${MEETING_ID}
2f550432-579c-4058-bbb9-be8a01d3beea
$ echo ${MEETING_ID_2}
2f550432-579c-4058-bbb9-be8a01d3beea
Now you can start online meeting using Amazon Chime SDK client-side implementation with this responded Meeting and Attendee data.
You can see sample single page application using Vue.js and Amazon Chime SDK for JavaScript as a part of example Rails application.
B: Develop your Rails Application with Action View
Let's start to building simple Rails application with Action View providing real-time communications in a private room. For example, create Rails application using Devise for user authentication.
Prepare Rails application
At first, create new Rails application:
$ rails new chime_view_app
$ cd chime_view_app
Add gems to your Gemfile:
# Gemfile
gem 'devise'
gem 'amazon-chime-sdk-rails'
Then, install devise:
$ bundle install
$ rails g devise:install
$ rails g devise User
$ rails g migration add_name_to_users name:string
$ rails g devise:views User
Update your application_controller.rb
like this:
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:name])
end
end
Add user name form to your app/views/users/registrations/new.html.erb
view template like this:
# app/views/users/registrations/new.html.erb
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name, autocomplete: "name" %>
</div>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
</div>
Update devise configuration in devise.rb
to use scoped views:
# config/initializers/devise.rb
Devise.setup do |config|
# Uncomment and update
config.scoped_views = true
end
Add login header to your application. Create new app/views/layouts/_header.html.erb
and update your app/views/layouts/application.html.erb
like this:
# app/views/layouts/_header.html.erb
<header>
<div>
<div>
<strong>Rails Application for Amazon Chime SDK Meeting (Rails App with Action View)</strong>
</div>
<div>
<% if user_signed_in? %>
<%= current_user.name %>
<%= link_to 'Logout', destroy_user_session_path, method: :delete %>
<% else %>
<%= link_to "Sign up", new_user_registration_path %>
<%= link_to 'Login', new_user_session_path %>
<% end %>
</div>
</div>
</header>
# app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
<head>
<title>Rails Application for Amazon Chime SDK Meeting</title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= yield(:javascript_pack_tag) %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
</head>
<body>
<div id="app">
<%= render 'layouts/header' %>
<%= yield %>
<div>
</body>
</html>
Create private room functions
Create MVC by generator:
$ rails g scaffold room name:string
$ rails g scaffold entry room:references user:references
$ rake db:migrate
Update your room.rb
like this:
# app/models/room.rb
class Room < ApplicationRecord
has_many :entries, dependent: :destroy
has_many :members, through: :entries, source: :user
def member?(user)
members.include?(user)
end
end
Add uniqueness validation to your entry.rb
like this:
# app/models/entry.rb
class Entry < ApplicationRecord
belongs_to :room
belongs_to :user
# Add uniqueness validation
validates :user, uniqueness: { scope: :room }
end
Update create and destroy method in your entries_controller.rb
like this:
# app/controllers/entries_controller.rb
class EntriesController < ApplicationController
before_action :authenticate_user!
before_action :set_room
before_action :set_entry, only: [:destroy]
# POST /entries
# POST /entries.json
def create
@entry = Entry.new(entry_params)
respond_to do |format|
if @entry.save
format.html { redirect_to @room, notice: 'Member was successfully added.' }
format.json { render :show, status: :created, location: @room }
else
format.html { redirect_to @room, notice: @entry.errors }
format.json { render json: @entry.errors, status: :unprocessable_entity }
end
end
end
# DELETE /entries/1
# DELETE /entries/1.json
def destroy
@entry.destroy
respond_to do |format|
format.html { redirect_to @room, notice: 'Member was successfully removed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_room
@room = Room.find(params[:room_id])
end
# Use callbacks to share common setup or constraints between actions.
def set_entry
@entry = Entry.find(params[:id])
end
# Only allow a list of trusted parameters through.
def entry_params
params.require(:entry).permit(:room_id, :user_id)
end
end
Develop meeting functions with amazon-chime-sdk-rails
Install amazon-chime-sdk-rails and generates your controllers:
$ rails g chime_sdk:install
$ rails g chime_sdk:controllers -r room
Add and uncomment several functions in generated meetings_controller.rb
and meeting_attendees_controller.rb
for your app configurations:
# app/controllers/api/meetings_controller.rb
class MeetingsController < ApplicationController
before_action :authenticate_user!
before_action :set_room
before_action :check_membership
include ChimeSdk::Controller::Meetings::Mixin
private
# Add
def set_room
@room = Room.find(params[:room_id])
end
# Add
def check_membership
unless @room.member?(current_user)
message = 'Unauthorized: you are not a member of this private room.'
redirect_to @room, notice: message
end
end
# Uncomment
def meeting_request_id
"PrivateRoom-#{@room.id}"
end
# Uncomment
def attendee_request_id
"User-#{current_user.id}"
end
# Uncomment
def application_meeting_metadata(meeting)
{
"MeetingType": "PrivateRoom",
"Room": @room
}
end
# Uncomment
def application_attendee_metadata(attendee)
user_id = attendee[:Attendee][:ExternalUserId].split('-')[3]
{
"AttendeeType": "User",
"User": User.find_by_id(user_id)
}
end
# Uncomment
def application_attendee_metadata(attendee)
user_id = attendee[:Attendee][:ExternalUserId].split('-')[3]
{
"AttendeeType": "User",
"User": User.find_by_id(user_id)
}
end
end
# app/controllers/api/meeting_attendees_controller.rb
class MeetingAttendeesController < ApplicationController
before_action :authenticate_user!
before_action :set_room
before_action :check_membership
include ChimeSdk::Controller::Attendees::Mixin
private
# Add
def set_room
@room = Room.find(params[:room_id])
end
# Add
def check_membership
unless @room.member?(current_user)
message = 'Unauthorized: you are not a member of this private room.'
redirect_to @room, notice: message
end
end
# Uncomment
def attendee_request_id
"User-#{current_user.id}"
end
# Uncomment
def application_attendee_metadata(attendee)
user_id = attendee[:Attendee][:ExternalUserId].split('-')[3]
{
"AttendeeType": "User",
"User": User.find_by_id(user_id)
}
end
end
Generates meeting views by amazon-chime-sdk-rails generator:
$ rails g chime_sdk:views
Simply customize your meeting view generated app/views/meetings/show.html.erb:
// app/views/meetings/show.html.erb
function showApplicationUserName(attendee) {
// Comment
// return attendee.Attendee.AttendeeId;
// Uncomment
return `${attendee.Attendee.ApplicationMetadata.User.name} (${attendee.Attendee.AttendeeId})`;
}
Bundle Amazon Chime SDK into single amazon-chime-sdk.min.js file and copy it to app/assets/javascripts by amazon-chime-sdk-rails generator:
$ rails g chime_sdk:js
Add amazon-chime-sdk.min.js to your Asset Pipeline:
# config/initializers/assets.rb
Rails.application.config.assets.precompile += %w( amazon-chime-sdk.min.js )
Then, add member management and meeting link to your room view:
# app/views/rooms/show.html.erb
<p id="notice"><%= notice %></p>
<p>
<strong>Name:</strong>
<%= @room.name %>
</p>
<p>
<strong>Private Meeting:</strong>
<p><%= link_to 'Show Meetings', room_meetings_path(@room) %></p>
<p><%= link_to 'Join the Meeting', room_meetings_path(@room), method: :post %></p>
</p>
<p>
<strong>Members:</strong>
<table>
<tbody>
<% @room.entries.each do |entry| %>
<tr>
<td><%= entry.user.name %></td>
<td><%= link_to 'Remove', [@room, entry], method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
</p>
<p>
<strong>Add members:</strong>
<%= form_for [@room, Entry.new] do |f| %>
<%= f.hidden_field :room_id, value: @room.id %>
<%= f.collection_select :user_id, User.all, :id, :name %>
<%= f.submit "Add" %>
<% end %>
</p>
<%= link_to 'Edit', edit_room_path(@room) %> |
<%= link_to 'Back', rooms_path %>
Update your routes.rb
like this:
# config/routes.rb
Rails.application.routes.draw do
root "rooms#index"
devise_for :users
resources :rooms do
resources :entries, only: [:create, :destroy]
resources :meetings, only: [:index, :show, :create, :destroy] do
resources :meeting_attendees, as: :attendees, path: :attendees, only: [:index, :show]
end
end
end
Note that you need to set up AWS credentials or IAM role for amazon-chime-sdk-rails. See Set up AWS credentials for more details.
Finally, start rails server:
$ rails server
Now ready to take off!
Start meeting with your Rails application
Access http://localhost:3000/ through your web browser.
Sign up users from Sign up header. For example, sign up ichiro as [email protected] and stephen as [email protected].
Create new room and add ichiro and stephen as a room member.
Now you can join the meeting from "Join the Meeting" link in your room view. Your rails application includes simple online meeting implementation using Amazon Chime SDK as a Rails view.
The meeting has been created
Ichiro and Stephen have joined the meeting
You should customize your meeting view using Amazon Chime SDK for JavaScript. See sample Rails application using Amazon Chime SDK for JavaScript as a part of example Rails application.
Documentation
See API Reference for more details.
Examples
See example Rails application in /spec/rails_app. You can run this example Rails application by the following steps:
$ git clone https://github.com/simukappu/amazon-chime-sdk-rails.git
$ cd amazon-chime-sdk-rails
$ bundle install
$ cd spec/rails_app
$ bin/rake db:migrate
$ bin/rake db:seed
$ bin/rails g chime_sdk:js
$ bin/rails server
License
amazon-chime-sdk-rails project rocks and uses MIT License.