Process incoming asciicast with AsciicastCreator
This commit is contained in:
parent
86a4662fe5
commit
59e31baa1b
@ -1,15 +1,13 @@
|
|||||||
class Api::AsciicastsController < ApplicationController
|
class Api::AsciicastsController < ApplicationController
|
||||||
|
|
||||||
skip_before_filter :verify_authenticity_token
|
skip_before_filter :verify_authenticity_token
|
||||||
|
|
||||||
def create
|
def create
|
||||||
ac = Asciicast.new(params[:asciicast])
|
asciicast = AsciicastCreator.new.create(params[:asciicast])
|
||||||
|
render :text => asciicast_url(asciicast), :status => :created,
|
||||||
if ac.save
|
:location => asciicast
|
||||||
SnapshotWorker.perform_async(ac.id)
|
rescue ActiveRecord::RecordInvalid => e
|
||||||
render :text => asciicast_url(ac), :status => :created, :location => ac
|
render :nothing => true, :status => 422
|
||||||
else
|
|
||||||
render :text => ac.errors.full_messages, :status => 422
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -29,8 +29,7 @@ class Asciicast < ActiveRecord::Base
|
|||||||
|
|
||||||
before_create :assign_user, :unless => :user
|
before_create :assign_user, :unless => :user
|
||||||
|
|
||||||
attr_accessible :meta, :stdout, :stdout_timing, :stdin, :stdin_timing,
|
attr_accessible :title, :description, :time_compression
|
||||||
:title, :description, :time_compression
|
|
||||||
|
|
||||||
def self.assign_user(user_token, user)
|
def self.assign_user(user_token, user)
|
||||||
where(:user_id => nil, :user_token => user_token).
|
where(:user_id => nil, :user_token => user_token).
|
||||||
@ -42,22 +41,6 @@ class Asciicast < ActiveRecord::Base
|
|||||||
Digest::MD5.hexdigest timestamps.join('/')
|
Digest::MD5.hexdigest timestamps.join('/')
|
||||||
end
|
end
|
||||||
|
|
||||||
def meta=(file)
|
|
||||||
data = JSON.parse(file.tempfile.read)
|
|
||||||
|
|
||||||
self.username = data['username']
|
|
||||||
self.user_token = data['user_token']
|
|
||||||
self.duration = data['duration']
|
|
||||||
self.recorded_at = data['recorded_at']
|
|
||||||
self.title = data['title']
|
|
||||||
self.command = data['command']
|
|
||||||
self.shell = data['shell']
|
|
||||||
self.uname = data['uname']
|
|
||||||
self.terminal_lines = data['term']['lines']
|
|
||||||
self.terminal_columns = data['term']['columns']
|
|
||||||
self.terminal_type = data['term']['type']
|
|
||||||
end
|
|
||||||
|
|
||||||
def assign_user
|
def assign_user
|
||||||
if user_token.present?
|
if user_token.present?
|
||||||
if ut = UserToken.find_by_token(user_token)
|
if ut = UserToken.find_by_token(user_token)
|
||||||
|
39
app/services/asciicast_creator.rb
Normal file
39
app/services/asciicast_creator.rb
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
class AsciicastCreator
|
||||||
|
|
||||||
|
def create(attributes)
|
||||||
|
attributes = prepare_attributes(attributes)
|
||||||
|
|
||||||
|
Asciicast.create!(attributes, :without_protection => true).tap do |asciicast|
|
||||||
|
SnapshotWorker.perform_async(asciicast.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def prepare_attributes(attributes)
|
||||||
|
meta = parse_meta_file(attributes[:meta])
|
||||||
|
|
||||||
|
{
|
||||||
|
:stdout_data => attributes[:stdout],
|
||||||
|
:stdout_timing => attributes[:stdout_timing],
|
||||||
|
:stdin_data => attributes[:stdin],
|
||||||
|
:stdin_timing => attributes[:stdin_timing],
|
||||||
|
:username => meta['username'],
|
||||||
|
:user_token => meta['user_token'],
|
||||||
|
:duration => meta['duration'],
|
||||||
|
:recorded_at => meta['recorded_at'],
|
||||||
|
:title => meta['title'],
|
||||||
|
:command => meta['command'],
|
||||||
|
:shell => meta['shell'],
|
||||||
|
:uname => meta['uname'],
|
||||||
|
:terminal_lines => meta['term']['lines'],
|
||||||
|
:terminal_columns => meta['term']['columns'],
|
||||||
|
:terminal_type => meta['term']['type'],
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse_meta_file(file)
|
||||||
|
JSON.parse(file.read)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
@ -3,57 +3,49 @@ require 'spec_helper'
|
|||||||
describe Api::AsciicastsController do
|
describe Api::AsciicastsController do
|
||||||
|
|
||||||
describe '#create' do
|
describe '#create' do
|
||||||
|
let(:creator) { double('creator') }
|
||||||
|
let(:attributes) { { 'foo' => 'bar' } }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(AsciicastCreator).to receive(:new).with(no_args()) { creator }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the creator returns an asciicast' do
|
||||||
let(:asciicast) { stub_model(Asciicast, :id => 666) }
|
let(:asciicast) { stub_model(Asciicast, :id => 666) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
new = asciicast
|
allow(creator).to receive(:create).with(attributes) { asciicast }
|
||||||
Asciicast.should_receive(:new).and_return(new)
|
post :create, :asciicast => attributes
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when save succeeds' do
|
it 'returns the status 201' do
|
||||||
|
expect(response.status).to eq(201)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns the URL of created asciicast as the content body' do
|
||||||
|
expect(response.body).to eq(asciicast_url(asciicast))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the creator raises ActiveRecord::RecordInvalid' do
|
||||||
|
let(:asciicast) { double('asciicast', :errors => errors) }
|
||||||
|
let(:errors) { double('errors', :full_messages => full_messages) }
|
||||||
|
let(:full_messages) { ['This is invalid'] }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
asciicast.stub(:save => true)
|
allow(creator).to receive(:create).with(attributes).
|
||||||
|
and_raise(ActiveRecord::RecordInvalid.new(asciicast))
|
||||||
|
post :create, :asciicast => attributes
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'enqueues snapshot capture' do
|
it 'returns the status 422' do
|
||||||
post :create
|
expect(response.status).to eq(422)
|
||||||
SnapshotWorker.should have_queued_job(asciicast.id)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns status 201' do
|
it 'returns the full error messages as the content body' do
|
||||||
post :create
|
expect(response.body).to be_blank
|
||||||
|
|
||||||
response.status.should == 201
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns URL of created asciicast as content body' do
|
|
||||||
post :create
|
|
||||||
|
|
||||||
response.body.should == asciicast_url(asciicast)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when save fails' do
|
|
||||||
before do
|
|
||||||
asciicast.stub(:save => false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns status 422' do
|
|
||||||
post :create
|
|
||||||
|
|
||||||
response.status.should == 422
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns full error messages as content body' do
|
|
||||||
full_messages = double.to_s
|
|
||||||
errors = double('errors', :full_messages => full_messages)
|
|
||||||
asciicast.should_receive(:errors).and_return(errors)
|
|
||||||
post :create
|
|
||||||
|
|
||||||
response.body.should == full_messages
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
15
spec/fixtures/meta.json
vendored
Normal file
15
spec/fixtures/meta.json
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"command": "/bin/bash",
|
||||||
|
"duration": 11.146430015563965,
|
||||||
|
"recorded_at": "Thu, 25 Jul 2013 20:08:57 +0000",
|
||||||
|
"shell": "/bin/zsh",
|
||||||
|
"term": {
|
||||||
|
"columns": 96,
|
||||||
|
"lines": 26,
|
||||||
|
"type": "screen-256color"
|
||||||
|
},
|
||||||
|
"title": "bashing :)",
|
||||||
|
"uname": "Linux 3.9.9-302.fc19.x86_64 #1 SMP Sat Jul 6 13:41:07 UTC 2013 x86_64",
|
||||||
|
"user_token": "f33e6188-f53c-11e2-abf4-84a6c827e88b",
|
||||||
|
"username": "kill"
|
||||||
|
}
|
@ -71,70 +71,6 @@ describe Asciicast do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#meta=' do
|
|
||||||
let(:asciicast) { stub_model(Asciicast) }
|
|
||||||
|
|
||||||
let(:username) { 'username' }
|
|
||||||
let(:user_token) { 'token' }
|
|
||||||
let(:duration) { 123.456 }
|
|
||||||
let(:recorded_at) { Time.now.to_s }
|
|
||||||
let(:title) { 'title' }
|
|
||||||
let(:command) { '/bin/command' }
|
|
||||||
let(:shell) { '/bin/shell' }
|
|
||||||
let(:uname) { 'OS' }
|
|
||||||
let(:terminal_lines) { 29 }
|
|
||||||
let(:terminal_columns) { 97 }
|
|
||||||
let(:terminal_type) { 'xterm-lolz' }
|
|
||||||
|
|
||||||
it 'assigns attributes properly' do
|
|
||||||
data = {
|
|
||||||
:username => username,
|
|
||||||
:user_token => user_token,
|
|
||||||
:duration => duration,
|
|
||||||
:recorded_at => recorded_at,
|
|
||||||
:title => title,
|
|
||||||
:command => command,
|
|
||||||
:shell => shell,
|
|
||||||
:uname => uname,
|
|
||||||
:term => {
|
|
||||||
:lines => terminal_lines,
|
|
||||||
:columns => terminal_columns,
|
|
||||||
:type => terminal_type,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
json = data.to_json
|
|
||||||
tempfile = stub('tempfile', :read => json)
|
|
||||||
json_file = stub('file', :tempfile => tempfile)
|
|
||||||
asciicast.meta = json_file
|
|
||||||
|
|
||||||
asciicast.username.should == username
|
|
||||||
asciicast.user_token.should == user_token
|
|
||||||
asciicast.duration.should == duration
|
|
||||||
asciicast.recorded_at.should == recorded_at
|
|
||||||
asciicast.title.should == title
|
|
||||||
asciicast.command.should == command
|
|
||||||
asciicast.shell.should == shell
|
|
||||||
asciicast.uname.should == uname
|
|
||||||
asciicast.terminal_lines.should == terminal_lines
|
|
||||||
asciicast.terminal_columns.should == terminal_columns
|
|
||||||
asciicast.terminal_type.should == terminal_type
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#snapshot' do
|
|
||||||
let(:asciicast) { Asciicast.new }
|
|
||||||
let(:snapshot) {
|
|
||||||
Snapshot.new([
|
|
||||||
SnapshotLine.new([
|
|
||||||
SnapshotFragment.new('foo', { :fg => 1 })
|
|
||||||
])
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
it 'is empty Snapshot instance initially' do
|
|
||||||
expect(asciicast.snapshot).to eq(Snapshot.new)
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#stdout' do
|
describe '#stdout' do
|
||||||
let(:stdout) { double('stdout') }
|
let(:stdout) { double('stdout') }
|
||||||
|
|
||||||
|
56
spec/services/asciicast_creator_spec.rb
Normal file
56
spec/services/asciicast_creator_spec.rb
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe AsciicastCreator do
|
||||||
|
let(:creator) { AsciicastCreator.new }
|
||||||
|
|
||||||
|
describe '#create' do
|
||||||
|
let(:meta_file) { fixture_file_upload('spec/fixtures/meta.json', 'application/json') }
|
||||||
|
let(:stdout_data_file) { double('stdout_data_file') }
|
||||||
|
let(:stdout_timing_file) { double('stdout_timing_file') }
|
||||||
|
let(:asciicast) { stub_model(Asciicast, :id => 666) }
|
||||||
|
|
||||||
|
subject {
|
||||||
|
creator.create(
|
||||||
|
:meta => meta_file,
|
||||||
|
:stdout => stdout_data_file,
|
||||||
|
:stdout_timing => stdout_timing_file
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(Asciicast).to receive(:create!) { asciicast }
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'calls Asciicast.create! with proper attributes' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(Asciicast).to have_received(:create!).with({
|
||||||
|
:stdout_data => stdout_data_file,
|
||||||
|
:stdout_timing => stdout_timing_file,
|
||||||
|
:stdin_data => nil,
|
||||||
|
:stdin_timing => nil,
|
||||||
|
:username => 'kill',
|
||||||
|
:user_token => 'f33e6188-f53c-11e2-abf4-84a6c827e88b',
|
||||||
|
:duration => 11.146430015563965,
|
||||||
|
:recorded_at => 'Thu, 25 Jul 2013 20:08:57 +0000',
|
||||||
|
:title => 'bashing :)',
|
||||||
|
:command => '/bin/bash',
|
||||||
|
:shell => '/bin/zsh',
|
||||||
|
:uname => 'Linux 3.9.9-302.fc19.x86_64 #1 SMP Sat Jul 6 13:41:07 UTC 2013 x86_64',
|
||||||
|
:terminal_columns => 96,
|
||||||
|
:terminal_lines => 26,
|
||||||
|
:terminal_type => 'screen-256color'
|
||||||
|
}, { :without_protection => true })
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'enqueues snapshot capture job' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(SnapshotWorker).to have_queued_job(asciicast.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns the created asciicast' do
|
||||||
|
expect(subject).to be(asciicast)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -10,8 +10,7 @@ module AsciiIo
|
|||||||
end
|
end
|
||||||
|
|
||||||
def load_asciicast(id)
|
def load_asciicast(id)
|
||||||
FactoryGirl.create(
|
AsciicastCreator.new.create(
|
||||||
:asciicast,
|
|
||||||
:meta => uploaded_file(
|
:meta => uploaded_file(
|
||||||
"spec/fixtures/asciicasts/#{id}/meta.json",
|
"spec/fixtures/asciicasts/#{id}/meta.json",
|
||||||
'application/json'
|
'application/json'
|
||||||
|
Loading…
Reference in New Issue
Block a user