forked from treehouse/mastodon
Refactoring Grape API methods into normal controllers & other things
parent
11ff92c9d7
commit
0e8f59c16f
1
Gemfile
1
Gemfile
|
@ -22,6 +22,7 @@ gem 'grape-entity'
|
||||||
gem 'hashie-forbidden_attributes'
|
gem 'hashie-forbidden_attributes'
|
||||||
gem 'paranoia', '~> 2.0'
|
gem 'paranoia', '~> 2.0'
|
||||||
gem 'paperclip', '~> 4.3'
|
gem 'paperclip', '~> 4.3'
|
||||||
|
gem 'backport_new_renderer'
|
||||||
|
|
||||||
gem 'http'
|
gem 'http'
|
||||||
gem 'addressable'
|
gem 'addressable'
|
||||||
|
|
|
@ -43,6 +43,8 @@ GEM
|
||||||
descendants_tracker (~> 0.0.4)
|
descendants_tracker (~> 0.0.4)
|
||||||
ice_nine (~> 0.11.0)
|
ice_nine (~> 0.11.0)
|
||||||
thread_safe (~> 0.3, >= 0.3.1)
|
thread_safe (~> 0.3, >= 0.3.1)
|
||||||
|
backport_new_renderer (1.0.0)
|
||||||
|
rails
|
||||||
better_errors (2.1.1)
|
better_errors (2.1.1)
|
||||||
coderay (>= 1.0.0)
|
coderay (>= 1.0.0)
|
||||||
erubis (>= 2.6.6)
|
erubis (>= 2.6.6)
|
||||||
|
@ -320,6 +322,7 @@ PLATFORMS
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
addressable
|
addressable
|
||||||
|
backport_new_renderer
|
||||||
better_errors
|
better_errors
|
||||||
binding_of_caller
|
binding_of_caller
|
||||||
coffee-rails (~> 4.1.0)
|
coffee-rails (~> 4.1.0)
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
module Mastodon
|
|
||||||
class API < Grape::API
|
|
||||||
rescue_from :all
|
|
||||||
|
|
||||||
mount Mastodon::Ostatus
|
|
||||||
mount Mastodon::Rest
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,54 +0,0 @@
|
||||||
module Mastodon
|
|
||||||
module Entities
|
|
||||||
class Account < Grape::Entity
|
|
||||||
include ApplicationHelper
|
|
||||||
|
|
||||||
expose :id
|
|
||||||
expose :username
|
|
||||||
|
|
||||||
expose :domain do |account|
|
|
||||||
account.local? ? LOCAL_DOMAIN : account.domain
|
|
||||||
end
|
|
||||||
|
|
||||||
expose :display_name
|
|
||||||
expose :note
|
|
||||||
|
|
||||||
expose :url do |account|
|
|
||||||
account.local? ? profile_url(name: account.username) : account.url
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Status < Grape::Entity
|
|
||||||
include ApplicationHelper
|
|
||||||
|
|
||||||
format_with(:iso_timestamp) { |dt| dt.iso8601 }
|
|
||||||
|
|
||||||
expose :id
|
|
||||||
|
|
||||||
expose :uri do |status|
|
|
||||||
status.local? ? unique_tag(status.stream_entry.created_at, status.stream_entry.activity_id, status.stream_entry.activity_type) : status.uri
|
|
||||||
end
|
|
||||||
|
|
||||||
expose :url do |status|
|
|
||||||
status.local? ? status_url(name: status.account.username, id: status.id) : status.url
|
|
||||||
end
|
|
||||||
|
|
||||||
expose :text
|
|
||||||
expose :in_reply_to_id
|
|
||||||
|
|
||||||
expose :reblog_of_id
|
|
||||||
expose :reblog, using: Mastodon::Entities::Status
|
|
||||||
|
|
||||||
expose :account, using: Mastodon::Entities::Account
|
|
||||||
|
|
||||||
with_options(format_with: :iso_timestamp) do
|
|
||||||
expose :created_at
|
|
||||||
expose :updated_at
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class StreamEntry < Grape::Entity
|
|
||||||
expose :activity, using: Mastodon::Entities::Status
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,62 +0,0 @@
|
||||||
module Mastodon
|
|
||||||
class Ostatus < Grape::API
|
|
||||||
format :txt
|
|
||||||
|
|
||||||
before do
|
|
||||||
@account = Account.find(params[:id])
|
|
||||||
end
|
|
||||||
|
|
||||||
resource :subscriptions do
|
|
||||||
helpers do
|
|
||||||
include ApplicationHelper
|
|
||||||
end
|
|
||||||
|
|
||||||
desc 'Receive updates from an account'
|
|
||||||
|
|
||||||
params do
|
|
||||||
requires :id, type: String, desc: 'Account ID'
|
|
||||||
end
|
|
||||||
|
|
||||||
post ':id' do
|
|
||||||
body = request.body.read
|
|
||||||
|
|
||||||
if @account.subscription(subscription_url(@account)).verify(body, env['HTTP_X_HUB_SIGNATURE'])
|
|
||||||
ProcessFeedService.new.(body, @account)
|
|
||||||
status 201
|
|
||||||
else
|
|
||||||
status 202
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
desc 'Confirm PuSH subscription to an account'
|
|
||||||
|
|
||||||
params do
|
|
||||||
requires :id, type: String, desc: 'Account ID'
|
|
||||||
requires 'hub.topic', type: String, desc: 'Topic URL'
|
|
||||||
requires 'hub.verify_token', type: String, desc: 'Verification token'
|
|
||||||
requires 'hub.challenge', type: String, desc: 'Hub challenge'
|
|
||||||
end
|
|
||||||
|
|
||||||
get ':id' do
|
|
||||||
if @account.subscription(subscription_url(@account)).valid?(params['hub.topic'], params['hub.verify_token'])
|
|
||||||
params['hub.challenge']
|
|
||||||
else
|
|
||||||
error! :not_found, 404
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
resource :salmon do
|
|
||||||
desc 'Receive Salmon updates targeted to account'
|
|
||||||
|
|
||||||
params do
|
|
||||||
requires :id, type: String, desc: 'Account ID'
|
|
||||||
end
|
|
||||||
|
|
||||||
post ':id' do
|
|
||||||
ProcessInteractionService.new.(request.body.read, @account)
|
|
||||||
status 201
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,44 +0,0 @@
|
||||||
module Mastodon
|
|
||||||
class Rest < Grape::API
|
|
||||||
version 'v1', using: :path
|
|
||||||
format :json
|
|
||||||
|
|
||||||
helpers do
|
|
||||||
def current_user
|
|
||||||
User.first
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
resource :timelines do
|
|
||||||
desc 'Return a public timeline'
|
|
||||||
|
|
||||||
get :public do
|
|
||||||
# todo
|
|
||||||
end
|
|
||||||
|
|
||||||
desc 'Return the home timeline of a logged in user'
|
|
||||||
|
|
||||||
get :home do
|
|
||||||
present current_user.timeline, with: Mastodon::Entities::StreamEntry
|
|
||||||
end
|
|
||||||
|
|
||||||
desc 'Return the notifications timeline of a logged in user'
|
|
||||||
|
|
||||||
get :notifications do
|
|
||||||
# todo
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
resource :accounts do
|
|
||||||
desc 'Return a user profile'
|
|
||||||
|
|
||||||
params do
|
|
||||||
requires :id, type: String, desc: 'Account ID'
|
|
||||||
end
|
|
||||||
|
|
||||||
get ':id' do
|
|
||||||
present Account.find(params[:id]), with: Mastodon::Entities::Account
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,3 +0,0 @@
|
||||||
# Place all the behaviors and hooks related to the matching controller here.
|
|
||||||
# All this logic will automatically be available in application.js.
|
|
||||||
# You can use CoffeeScript in this file: http://coffeescript.org/
|
|
|
@ -1,3 +0,0 @@
|
||||||
# Place all the behaviors and hooks related to the matching controller here.
|
|
||||||
# All this logic will automatically be available in application.js.
|
|
||||||
# You can use CoffeeScript in this file: http://coffeescript.org/
|
|
|
@ -1,3 +0,0 @@
|
||||||
# Place all the behaviors and hooks related to the matching controller here.
|
|
||||||
# All this logic will automatically be available in application.js.
|
|
||||||
# You can use CoffeeScript in this file: http://coffeescript.org/
|
|
|
@ -1,3 +0,0 @@
|
||||||
# Place all the behaviors and hooks related to the matching controller here.
|
|
||||||
# All this logic will automatically be available in application.js.
|
|
||||||
# You can use CoffeeScript in this file: http://coffeescript.org/
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
.card {
|
||||||
|
display: flex;
|
||||||
|
background: $primary-color;
|
||||||
|
box-shadow: 4px 3px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
.bio {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 18px * 1.5;
|
||||||
|
color: $quaternary-color;
|
||||||
|
|
||||||
|
small {
|
||||||
|
display: block;
|
||||||
|
font-size: 14px;
|
||||||
|
color: $quaternary-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
width: 96px;
|
||||||
|
float: left;
|
||||||
|
margin-right: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
padding-right: 0;
|
||||||
|
padding-left: 9px;
|
||||||
|
margin-top: -30px;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 94px;
|
||||||
|
height: 94px;
|
||||||
|
display: block;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: 4px 3px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,3 @@
|
||||||
// Place all the styles related to the Atom controller here.
|
// Place all the styles related to the API::Salmon controller here.
|
||||||
// They will automatically be included in application.css.
|
// They will automatically be included in application.css.
|
||||||
// You can use Sass (SCSS) here: http://sass-lang.com/
|
// You can use Sass (SCSS) here: http://sass-lang.com/
|
|
@ -1,3 +1,3 @@
|
||||||
// Place all the styles related to the XRD controller here.
|
// Place all the styles related to the API::Subscriptions controller here.
|
||||||
// They will automatically be included in application.css.
|
// They will automatically be included in application.css.
|
||||||
// You can use Sass (SCSS) here: http://sass-lang.com/
|
// You can use Sass (SCSS) here: http://sass-lang.com/
|
|
@ -20,7 +20,7 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
width: 800px;
|
width: 700px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
margin-top: 40px;
|
margin-top: 40px;
|
||||||
}
|
}
|
||||||
|
@ -40,4 +40,5 @@ body {
|
||||||
|
|
||||||
|
|
||||||
@import 'home';
|
@import 'home';
|
||||||
@import 'profile';
|
@import 'accounts';
|
||||||
|
@import 'stream_entries';
|
||||||
|
|
|
@ -1,59 +1,32 @@
|
||||||
.card {
|
|
||||||
display: flex;
|
|
||||||
background: $darker-background-color;
|
|
||||||
border: 1px solid darken($darker-background-color, 15%);
|
|
||||||
box-shadow: 4px 3px 0 rgba(0, 0, 0, 0.1);
|
|
||||||
|
|
||||||
.bio {
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.name {
|
|
||||||
font-size: 24px;
|
|
||||||
line-height: 18px * 1.5;
|
|
||||||
color: $text-color;
|
|
||||||
|
|
||||||
small {
|
|
||||||
display: block;
|
|
||||||
font-size: 14px;
|
|
||||||
color: $lighter-text-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar {
|
|
||||||
width: 96px;
|
|
||||||
float: left;
|
|
||||||
margin-right: 10px;
|
|
||||||
padding: 10px;
|
|
||||||
padding-left: 9px;
|
|
||||||
margin-top: -30px;
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 94px;
|
|
||||||
height: 94px;
|
|
||||||
display: block;
|
|
||||||
border: 2px solid $lighter-text-color;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-stream {
|
.activity-stream {
|
||||||
clear: both;
|
clear: both;
|
||||||
box-shadow: 4px 3px 0 rgba(0, 0, 0, 0.1);
|
box-shadow: 4px 3px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
.entry {
|
.entry {
|
||||||
border-bottom: 1px solid darken($background-color, 10%);
|
border-bottom: 1px solid $darker-background-color;
|
||||||
background: $background-color;
|
background: $background-color;
|
||||||
border-left: 2px solid $primary-color;
|
border-left: 2px solid $primary-color;
|
||||||
|
|
||||||
&.entry-reblog {
|
&.entry-reblog {
|
||||||
border-left: 2px solid $tertiary-color;
|
border-left: 2px solid $tertiary-color;
|
||||||
|
|
||||||
|
.content {
|
||||||
|
a {
|
||||||
|
color: $tertiary-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.entry-predecessor, &.entry-successor {
|
&.entry-predecessor, &.entry-successor {
|
||||||
border-left: 2px solid $lighter-text-color;
|
border-left: 2px solid $lighter-text-color;
|
||||||
background: darken($background-color, 5%);
|
background: darken($background-color, 5%);
|
||||||
|
|
||||||
|
.content {
|
||||||
|
a {
|
||||||
|
color: $lighter-text-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.entry-follow, &.entry-favourite {
|
&.entry-follow, &.entry-favourite {
|
||||||
|
@ -92,9 +65,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 5px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
|
padding-left: 8px;
|
||||||
|
|
||||||
.name {
|
.name {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
@ -113,7 +87,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.pre-header {
|
.pre-header {
|
||||||
border-bottom: 1px solid darken($background-color, 10%);
|
border-bottom: 1px solid darken($background-color, 5%);
|
||||||
color: $tertiary-color;
|
color: $tertiary-color;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
padding-left: 8px;
|
padding-left: 8px;
|
||||||
|
@ -131,9 +105,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
font-size: 16px;
|
font-size: 14px;
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
padding-left: 8px;
|
padding-left: 8px;
|
||||||
|
padding-bottom: 25px;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: $primary-color;
|
color: $primary-color;
|
||||||
|
@ -153,24 +128,4 @@
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.counters {
|
|
||||||
margin-top: 15px;
|
|
||||||
color: $lighter-text-color;
|
|
||||||
cursor: default;
|
|
||||||
padding: 10px;
|
|
||||||
padding-top: 0;
|
|
||||||
|
|
||||||
.counter {
|
|
||||||
display: inline-block;
|
|
||||||
margin-right: 10px;
|
|
||||||
color: $lighter-text-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.conversation-link {
|
|
||||||
color: $primary-color;
|
|
||||||
text-decoration: underline;
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
class AccountsController < ApplicationController
|
||||||
|
before_action :set_account
|
||||||
|
|
||||||
|
def show
|
||||||
|
respond_to do |format|
|
||||||
|
format.html
|
||||||
|
format.atom
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_account
|
||||||
|
@account = Account.find_by!(username: params[:username], domain: nil)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,14 @@
|
||||||
|
class Api::SalmonController < ApplicationController
|
||||||
|
before_action :set_account
|
||||||
|
|
||||||
|
def update
|
||||||
|
ProcessInteractionService.new.(request.body.read, @account)
|
||||||
|
render nothing: true, status: 201
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_account
|
||||||
|
@account = Account.find(params[:id])
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,28 @@
|
||||||
|
class Api::SubscriptionsController < ApplicationController
|
||||||
|
before_action :set_account
|
||||||
|
|
||||||
|
def show
|
||||||
|
if @account.subscription(api_subscription_url(@account.id)).valid?(params['hub.topic'], params['hub.verify_token'])
|
||||||
|
render text: params['hub.challenge'], status: 200
|
||||||
|
else
|
||||||
|
render nothing: true, status: 404
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
body = request.body.read
|
||||||
|
|
||||||
|
if @account.subscription(api_subscription_url(@account.id)).verify(body, env['HTTP_X_HUB_SIGNATURE'])
|
||||||
|
ProcessFeedService.new.(body, @account)
|
||||||
|
render nothing: true, status: 201
|
||||||
|
else
|
||||||
|
render nothing: true, status: 202
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_account
|
||||||
|
@account = Account.find(params[:id])
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,18 +0,0 @@
|
||||||
class AtomController < ApplicationController
|
|
||||||
before_filter :set_format
|
|
||||||
|
|
||||||
def user_stream
|
|
||||||
@account = Account.find_by!(id: params[:id], domain: nil)
|
|
||||||
end
|
|
||||||
|
|
||||||
def entry
|
|
||||||
@entry = StreamEntry.find(params[:id])
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_format
|
|
||||||
request.format = 'xml'
|
|
||||||
response.headers['Content-Type'] = 'application/atom+xml'
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,17 +0,0 @@
|
||||||
class ProfileController < ApplicationController
|
|
||||||
before_action :set_account
|
|
||||||
|
|
||||||
def show
|
|
||||||
end
|
|
||||||
|
|
||||||
def entry
|
|
||||||
@entry = @account.stream_entries.find(params[:id])
|
|
||||||
@type = @entry.activity_type.downcase
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_account
|
|
||||||
@account = Account.find_by!(username: params[:name], domain: nil)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
class StreamEntriesController < ApplicationController
|
||||||
|
before_action :set_account
|
||||||
|
before_action :set_stream_entry
|
||||||
|
|
||||||
|
def show
|
||||||
|
@type = @stream_entry.activity_type.downcase
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.html
|
||||||
|
format.atom
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_account
|
||||||
|
@account = Account.find_by!(username: params[:account_username], domain: nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_stream_entry
|
||||||
|
@stream_entry = @account.stream_entries.find(params[:id])
|
||||||
|
end
|
||||||
|
end
|
|
@ -9,6 +9,8 @@ class XrdController < ApplicationController
|
||||||
@account = Account.find_by!(username: username_from_resource, domain: nil)
|
@account = Account.find_by!(username: username_from_resource, domain: nil)
|
||||||
@canonical_account_uri = "acct:#{@account.username}@#{LOCAL_DOMAIN}"
|
@canonical_account_uri = "acct:#{@account.username}@#{LOCAL_DOMAIN}"
|
||||||
@magic_key = pem_to_magic_key(@account.keypair.public_key)
|
@magic_key = pem_to_magic_key(@account.keypair.public_key)
|
||||||
|
rescue ActiveRecord::RecordNotFound
|
||||||
|
render nothing: true, status: 404
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
module AccountsHelper
|
||||||
|
|
||||||
|
end
|
|
@ -0,0 +1,2 @@
|
||||||
|
module Api::SalmonHelper
|
||||||
|
end
|
|
@ -0,0 +1,2 @@
|
||||||
|
module Api::SubscriptionsHelper
|
||||||
|
end
|
|
@ -1,6 +1,4 @@
|
||||||
module ApplicationHelper
|
module ApplicationHelper
|
||||||
include RoutingHelper
|
|
||||||
|
|
||||||
def unique_tag(date, id, type)
|
def unique_tag(date, id, type)
|
||||||
"tag:#{LOCAL_DOMAIN},#{date.strftime('%Y-%m-%d')}:objectId=#{id}:objectType=#{type}"
|
"tag:#{LOCAL_DOMAIN},#{date.strftime('%Y-%m-%d')}:objectId=#{id}:objectType=#{type}"
|
||||||
end
|
end
|
||||||
|
@ -13,24 +11,4 @@ module ApplicationHelper
|
||||||
def local_id?(id)
|
def local_id?(id)
|
||||||
id.start_with?("tag:#{LOCAL_DOMAIN}")
|
id.start_with?("tag:#{LOCAL_DOMAIN}")
|
||||||
end
|
end
|
||||||
|
|
||||||
def subscription_url(account)
|
|
||||||
add_base_url_prefix subscriptions_path(id: account.id, format: '')
|
|
||||||
end
|
|
||||||
|
|
||||||
def salmon_url(account)
|
|
||||||
add_base_url_prefix salmon_path(id: account.id, format: '')
|
|
||||||
end
|
|
||||||
|
|
||||||
def profile_url(account)
|
|
||||||
account.local? ? super(name: account.username) : account.url
|
|
||||||
end
|
|
||||||
|
|
||||||
def status_url(status)
|
|
||||||
status.local? ? super(name: status.account.username, id: status.stream_entry.id) : status.url
|
|
||||||
end
|
|
||||||
|
|
||||||
def add_base_url_prefix(suffix)
|
|
||||||
File.join(root_url, "api", suffix)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
module AtomHelper
|
module AtomBuilderHelper
|
||||||
def stream_updated_at
|
def stream_updated_at
|
||||||
@account.stream_entries.last ? (@account.updated_at > @account.stream_entries.last.created_at ? @account.updated_at : @account.stream_entries.last.created_at) : @account.updated_at
|
@account.stream_entries.last ? (@account.updated_at > @account.stream_entries.last.created_at ? @account.updated_at : @account.stream_entries.last.created_at) : @account.updated_at
|
||||||
end
|
end
|
||||||
|
@ -97,10 +97,10 @@ module AtomHelper
|
||||||
xml['thr'].send('in-reply-to', { ref: uri, href: url, type: 'text/html' })
|
xml['thr'].send('in-reply-to', { ref: uri, href: url, type: 'text/html' })
|
||||||
end
|
end
|
||||||
|
|
||||||
def disambiguate_uri(target)
|
def uri_for_target(target)
|
||||||
if target.local?
|
if target.local?
|
||||||
if target.object_type == :person
|
if target.object_type == :person
|
||||||
profile_url(target)
|
account_url(target)
|
||||||
else
|
else
|
||||||
unique_tag(target.stream_entry.created_at, target.stream_entry.activity_id, target.stream_entry.activity_type)
|
unique_tag(target.stream_entry.created_at, target.stream_entry.activity_id, target.stream_entry.activity_type)
|
||||||
end
|
end
|
||||||
|
@ -109,12 +109,12 @@ module AtomHelper
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def disambiguate_url(target)
|
def url_for_target(target)
|
||||||
if target.local?
|
if target.local?
|
||||||
if target.object_type == :person
|
if target.object_type == :person
|
||||||
profile_url(target)
|
account_url(target)
|
||||||
else
|
else
|
||||||
status_url(target)
|
account_stream_entry_url(target.account, target.stream_entry)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
target.url
|
target.url
|
||||||
|
@ -122,13 +122,13 @@ module AtomHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def link_mention(xml, account)
|
def link_mention(xml, account)
|
||||||
xml.link(rel: 'mentioned', href: disambiguate_uri(account))
|
xml.link(rel: 'mentioned', href: uri_for_target(account))
|
||||||
end
|
end
|
||||||
|
|
||||||
def link_avatar(xml, account)
|
def link_avatar(xml, account)
|
||||||
xml.link('rel' => 'avatar', 'type' => account.avatar_content_type, 'media:width' => '300', 'media:height' =>'300', 'href' => asset_url(account.avatar.url(:large)))
|
xml.link('rel' => 'avatar', 'type' => account.avatar_content_type, 'media:width' => '300', 'media:height' =>'300', 'href' => asset_url(account.avatar.url(:large, false)))
|
||||||
xml.link('rel' => 'avatar', 'type' => account.avatar_content_type, 'media:width' => '96', 'media:height' =>'96', 'href' => asset_url(account.avatar.url(:medium)))
|
xml.link('rel' => 'avatar', 'type' => account.avatar_content_type, 'media:width' => '96', 'media:height' =>'96', 'href' => asset_url(account.avatar.url(:medium, false)))
|
||||||
xml.link('rel' => 'avatar', 'type' => account.avatar_content_type, 'media:width' => '48', 'media:height' =>'48', 'href' => asset_url(account.avatar.url(:small)))
|
xml.link('rel' => 'avatar', 'type' => account.avatar_content_type, 'media:width' => '48', 'media:height' =>'48', 'href' => asset_url(account.avatar.url(:small, false)))
|
||||||
end
|
end
|
||||||
|
|
||||||
def logo(xml, url)
|
def logo(xml, url)
|
||||||
|
@ -137,10 +137,10 @@ module AtomHelper
|
||||||
|
|
||||||
def include_author(xml, account)
|
def include_author(xml, account)
|
||||||
object_type xml, :person
|
object_type xml, :person
|
||||||
uri xml, profile_url(account)
|
uri xml, url_for_target(account)
|
||||||
name xml, account.username
|
name xml, account.username
|
||||||
summary xml, account.note
|
summary xml, account.note
|
||||||
link_alternate xml, profile_url(account)
|
link_alternate xml, url_for_target(account)
|
||||||
link_avatar xml, account
|
link_avatar xml, account
|
||||||
portable_contact xml, account
|
portable_contact xml, account
|
||||||
end
|
end
|
||||||
|
@ -152,20 +152,20 @@ module AtomHelper
|
||||||
title xml, stream_entry.title
|
title xml, stream_entry.title
|
||||||
content xml, stream_entry.content
|
content xml, stream_entry.content
|
||||||
verb xml, stream_entry.verb
|
verb xml, stream_entry.verb
|
||||||
link_self xml, atom_entry_url(id: stream_entry.id)
|
link_self xml, account_stream_entry_url(stream_entry.account, stream_entry, format: 'atom')
|
||||||
object_type xml, stream_entry.object_type
|
object_type xml, stream_entry.object_type
|
||||||
|
|
||||||
# Comments need thread element
|
# Comments need thread element
|
||||||
if stream_entry.threaded?
|
if stream_entry.threaded?
|
||||||
in_reply_to xml, disambiguate_uri(stream_entry.thread), disambiguate_url(stream_entry.thread)
|
in_reply_to xml, uri_for_target(stream_entry.thread), url_for_target(stream_entry.thread)
|
||||||
end
|
end
|
||||||
|
|
||||||
if stream_entry.targeted?
|
if stream_entry.targeted?
|
||||||
target(xml) do
|
target(xml) do
|
||||||
object_type xml, stream_entry.target.object_type
|
object_type xml, stream_entry.target.object_type
|
||||||
simple_id xml, disambiguate_uri(stream_entry.target)
|
simple_id xml, uri_for_target(stream_entry.target)
|
||||||
title xml, stream_entry.target.title
|
title xml, stream_entry.target.title
|
||||||
link_alternate xml, disambiguate_url(stream_entry.target)
|
link_alternate xml, url_for_target(stream_entry.target)
|
||||||
|
|
||||||
# People have summary and portable contacts information
|
# People have summary and portable contacts information
|
||||||
if stream_entry.target.object_type == :person
|
if stream_entry.target.object_type == :person
|
|
@ -1,4 +1,4 @@
|
||||||
module ProfileHelper
|
module StreamEntriesHelper
|
||||||
def display_name(account)
|
def display_name(account)
|
||||||
account.display_name.blank? ? account.username : account.display_name
|
account.display_name.blank? ? account.username : account.display_name
|
||||||
end
|
end
|
|
@ -76,6 +76,10 @@ class Account < ActiveRecord::Base
|
||||||
@avatar_remote_url = url
|
@avatar_remote_url = url
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def to_param
|
||||||
|
self.username
|
||||||
|
end
|
||||||
|
|
||||||
before_create do
|
before_create do
|
||||||
if local?
|
if local?
|
||||||
keypair = OpenSSL::PKey::RSA.new(Rails.env.test? ? 1024 : 2048)
|
keypair = OpenSSL::PKey::RSA.new(Rails.env.test? ? 1024 : 2048)
|
||||||
|
|
|
@ -1,9 +1,4 @@
|
||||||
class User < ActiveRecord::Base
|
class User < ActiveRecord::Base
|
||||||
belongs_to :account, inverse_of: :user
|
belongs_to :account, inverse_of: :user
|
||||||
|
|
||||||
validates :account, presence: true
|
validates :account, presence: true
|
||||||
|
|
||||||
def timeline
|
|
||||||
StreamEntry.where(account_id: self.account.following, activity_type: 'Status').order('id desc')
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
class BaseService
|
class BaseService
|
||||||
|
include RoutingHelper
|
||||||
include ApplicationHelper
|
include ApplicationHelper
|
||||||
end
|
end
|
||||||
|
|
|
@ -38,7 +38,7 @@ class FollowRemoteAccountService < BaseService
|
||||||
account.secret = SecureRandom.hex
|
account.secret = SecureRandom.hex
|
||||||
account.verify_token = SecureRandom.hex
|
account.verify_token = SecureRandom.hex
|
||||||
|
|
||||||
subscription = account.subscription(subscription_url(account))
|
subscription = account.subscription(api_subscription_url(account.id))
|
||||||
subscription.subscribe
|
subscription.subscribe
|
||||||
|
|
||||||
account.save!
|
account.save!
|
||||||
|
|
|
@ -9,7 +9,7 @@ class FollowService < BaseService
|
||||||
|
|
||||||
follow = source_account.follow!(target_account)
|
follow = source_account.follow!(target_account)
|
||||||
send_interaction_service.(follow.stream_entry, target_account)
|
send_interaction_service.(follow.stream_entry, target_account)
|
||||||
source_account.ping!(atom_user_stream_url(id: source_account.id), [HUB_URL])
|
source_account.ping!(account_url(account, format: 'atom'), [HUB_URL])
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -7,7 +7,7 @@ class PostStatusService < BaseService
|
||||||
def call(account, text, in_reply_to = nil)
|
def call(account, text, in_reply_to = nil)
|
||||||
status = account.statuses.create!(text: text, thread: in_reply_to)
|
status = account.statuses.create!(text: text, thread: in_reply_to)
|
||||||
process_mentions_service.(status)
|
process_mentions_service.(status)
|
||||||
account.ping!(atom_user_stream_url(id: account.id), [HUB_URL])
|
account.ping!(account_url(account, format: 'atom'), [HUB_URL])
|
||||||
status
|
status
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ class ProcessInteractionService < BaseService
|
||||||
end
|
end
|
||||||
|
|
||||||
def mentions_account?(xml, account)
|
def mentions_account?(xml, account)
|
||||||
xml.xpath('/xmlns:entry/xmlns:link[@rel="mentioned"]').each { |mention_link| return true if mention_link.attribute('href').value == profile_url(account) }
|
xml.xpath('/xmlns:entry/xmlns:link[@rel="mentioned"]').each { |mention_link| return true if mention_link.attribute('href').value == url_for_target(account) }
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ class ReblogService < BaseService
|
||||||
# @return [Status]
|
# @return [Status]
|
||||||
def call(account, reblogged_status)
|
def call(account, reblogged_status)
|
||||||
reblog = account.statuses.create!(reblog: reblogged_status, text: '')
|
reblog = account.statuses.create!(reblog: reblogged_status, text: '')
|
||||||
account.ping!(atom_user_stream_url(id: account.id), [HUB_URL])
|
account.ping!(account_url(account, format: 'atom'), [HUB_URL])
|
||||||
return reblog if reblogged_status.local?
|
return reblog if reblogged_status.local?
|
||||||
send_interaction_service.(reblog.stream_entry, reblogged_status.account)
|
send_interaction_service.(reblog.stream_entry, reblogged_status.account)
|
||||||
reblog
|
reblog
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
class SendInteractionService < BaseService
|
class SendInteractionService < BaseService
|
||||||
include AtomHelper
|
include AtomBuilderHelper
|
||||||
|
|
||||||
# Send an Atom representation of an interaction to a remote Salmon endpoint
|
# Send an Atom representation of an interaction to a remote Salmon endpoint
|
||||||
# @param [StreamEntry] stream_entry
|
# @param [StreamEntry] stream_entry
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
Nokogiri::XML::Builder.new do |xml|
|
Nokogiri::XML::Builder.new do |xml|
|
||||||
feed(xml) do
|
feed(xml) do
|
||||||
simple_id xml, atom_user_stream_url(id: @account.id)
|
simple_id xml, account_url(@account, format: 'atom')
|
||||||
title xml, @account.display_name
|
title xml, @account.display_name
|
||||||
subtitle xml, @account.note
|
subtitle xml, @account.note
|
||||||
updated_at xml, stream_updated_at
|
updated_at xml, stream_updated_at
|
||||||
|
@ -10,10 +10,10 @@ Nokogiri::XML::Builder.new do |xml|
|
||||||
include_author xml, @account
|
include_author xml, @account
|
||||||
end
|
end
|
||||||
|
|
||||||
link_alternate xml, profile_url(@account)
|
link_alternate xml, url_for_target(@account)
|
||||||
link_self xml, atom_user_stream_url(id: @account.id)
|
link_self xml, account_url(@account, format: 'atom')
|
||||||
link_hub xml, HUB_URL
|
link_hub xml, HUB_URL
|
||||||
link_salmon xml, salmon_url(@account)
|
link_salmon xml, api_salmon_url(@account.id)
|
||||||
|
|
||||||
@account.stream_entries.order('id desc').each do |stream_entry|
|
@account.stream_entries.order('id desc').each do |stream_entry|
|
||||||
entry(xml, false) do
|
entry(xml, false) do
|
|
@ -1,6 +1,6 @@
|
||||||
- content_for :header_tags do
|
- content_for :header_tags do
|
||||||
%link{ rel: 'salmon', href: salmon_url(@account) }/
|
%link{ rel: 'salmon', href: api_salmon_url(@account.id) }/
|
||||||
%link{ rel: 'alternate', type: 'application/atom+xml', href: atom_user_stream_url(id: @account.id) }/
|
%link{ rel: 'alternate', type: 'application/atom+xml', href: account_url(@account, format: 'atom') }/
|
||||||
|
|
||||||
.card
|
.card
|
||||||
.avatar= image_tag @account.avatar.url(:medium)
|
.avatar= image_tag @account.avatar.url(:medium)
|
||||||
|
@ -11,4 +11,4 @@
|
||||||
|
|
||||||
.activity-stream
|
.activity-stream
|
||||||
- @account.statuses.order('id desc').each do |status|
|
- @account.statuses.order('id desc').each do |status|
|
||||||
= render partial: 'status', locals: { status: status, include_threads: false, is_successor: false, is_predecessor: false }
|
= render partial: 'stream_entries/status', locals: { status: status, include_threads: false, is_successor: false, is_predecessor: false }
|
|
@ -1,2 +0,0 @@
|
||||||
- if status.reply?
|
|
||||||
= link_to "In response to #{status.thread.account.acct}", status_url(status.thread), class: 'conversation-link'
|
|
|
@ -1,7 +0,0 @@
|
||||||
= link_to profile_url(status.account), class: 'name' do
|
|
||||||
%strong= display_name(status.account)
|
|
||||||
= "@#{status.account.acct}"
|
|
||||||
|
|
||||||
= link_to status_url(status), class: 'time' do
|
|
||||||
%span{ title: status.created_at }
|
|
||||||
= relative_time(status.created_at)
|
|
|
@ -1,5 +0,0 @@
|
||||||
- content_for :header_tags do
|
|
||||||
%link{ rel: 'alternate', type: 'application/atom+xml', href: atom_entry_url(id: @entry.id) }/
|
|
||||||
|
|
||||||
.activity-stream
|
|
||||||
= render partial: @type, locals: { @type.to_sym => @entry.activity, include_threads: true, is_predecessor: false, is_successor: false }
|
|
|
@ -6,17 +6,23 @@
|
||||||
.pre-header
|
.pre-header
|
||||||
%i.fa.fa-retweet
|
%i.fa.fa-retweet
|
||||||
Shared by
|
Shared by
|
||||||
= link_to display_name(status.account), profile_url(status.account), class: 'name'
|
= link_to display_name(status.account), url_for_target(status.account), class: 'name'
|
||||||
|
|
||||||
.entry__container
|
.entry__container
|
||||||
.avatar
|
.avatar
|
||||||
= image_tag avatar_for_status_url(status)
|
= image_tag avatar_for_status_url(status)
|
||||||
|
|
||||||
.entry__container__container
|
.entry__container__container
|
||||||
.header
|
.header
|
||||||
= render partial: 'status_header', locals: { status: status.reblog? ? status.reblog : status }
|
= link_to url_for_target(status.reblog? ? status.reblog.account : status.account), class: 'name' do
|
||||||
|
%strong= display_name(status.reblog? ? status.reblog.account : status.account)
|
||||||
|
= "@#{status.reblog? ? status.reblog.account.acct : status.account.acct}"
|
||||||
|
= link_to url_for_target(status.reblog? ? status.reblog : status), class: 'time' do
|
||||||
|
%span{ title: status.reblog? ? status.reblog.created_at : status.created_at }
|
||||||
|
= relative_time(status.reblog? ? status.reblog.created_at : status.created_at)
|
||||||
|
|
||||||
.content
|
.content
|
||||||
= status.content.html_safe
|
= status.content.html_safe
|
||||||
.counters
|
|
||||||
= render partial: 'status_footer', locals: { status: status.reblog? ? status.reblog : status }
|
|
||||||
|
|
||||||
- if include_threads
|
- if include_threads
|
||||||
- status.replies.each do |status|
|
- status.replies.each do |status|
|
|
@ -0,0 +1,5 @@
|
||||||
|
- content_for :header_tags do
|
||||||
|
%link{ rel: 'alternate', type: 'application/atom+xml', href: account_stream_entry_url(@account, @stream_entry, format: 'atom') }/
|
||||||
|
|
||||||
|
.activity-stream
|
||||||
|
= render partial: @type, locals: { @type.to_sym => @stream_entry.activity, include_threads: true, is_predecessor: false, is_successor: false }
|
|
@ -1,10 +1,10 @@
|
||||||
Nokogiri::XML::Builder.new do |xml|
|
Nokogiri::XML::Builder.new do |xml|
|
||||||
xml.XRD(xmlns: 'http://docs.oasis-open.org/ns/xri/xrd-1.0') do
|
xml.XRD(xmlns: 'http://docs.oasis-open.org/ns/xri/xrd-1.0') do
|
||||||
xml.Subject @canonical_account_uri
|
xml.Subject @canonical_account_uri
|
||||||
xml.Alias profile_url(@account)
|
xml.Alias url_for_target(@account)
|
||||||
xml.Link(rel: 'http://webfinger.net/rel/profile-page', type: 'text/html', href: profile_url(@account))
|
xml.Link(rel: 'http://webfinger.net/rel/profile-page', type: 'text/html', href: url_for_target(@account))
|
||||||
xml.Link(rel: 'http://schemas.google.com/g/2010#updates-from', type: 'application/atom+xml', href: atom_user_stream_url(id: @account.id))
|
xml.Link(rel: 'http://schemas.google.com/g/2010#updates-from', type: 'application/atom+xml', href: atom_user_stream_url(id: @account.id))
|
||||||
xml.Link(rel: 'salmon', href: salmon_url(@account))
|
xml.Link(rel: 'salmon', href: api_salmon_url(@account.id))
|
||||||
xml.Link(rel: 'magic-public-key', href: @magic_key)
|
xml.Link(rel: 'magic-public-key', href: @magic_key)
|
||||||
end
|
end
|
||||||
end.to_xml
|
end.to_xml
|
||||||
|
|
|
@ -2,12 +2,15 @@ Rails.application.routes.draw do
|
||||||
get '.well-known/host-meta', to: 'xrd#host_meta', as: :host_meta
|
get '.well-known/host-meta', to: 'xrd#host_meta', as: :host_meta
|
||||||
get '.well-known/webfinger', to: 'xrd#webfinger', as: :webfinger
|
get '.well-known/webfinger', to: 'xrd#webfinger', as: :webfinger
|
||||||
|
|
||||||
get 'atom/entries/:id', to: 'atom#entry', as: :atom_entry
|
resources :accounts, path: 'users', only: [:show], param: :username do
|
||||||
get 'atom/users/:id', to: 'atom#user_stream', as: :atom_user_stream
|
resources :stream_entries, path: 'updates', only: [:show]
|
||||||
get 'users/:name', to: 'profile#show', as: :profile
|
end
|
||||||
get 'users/:name/:id', to: 'profile#entry', as: :status
|
|
||||||
|
|
||||||
mount Mastodon::API => '/api/'
|
namespace :api do
|
||||||
|
resources :subscriptions, only: [:show]
|
||||||
|
post '/subscriptions/:id', to: 'subscriptions#update'
|
||||||
|
post '/salmon/:id', to: 'salmon#update', as: :salmon
|
||||||
|
end
|
||||||
|
|
||||||
root 'home#index'
|
root 'home#index'
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe AccountsController, type: :controller do
|
||||||
|
let(:alice) { Fabricate(:account, username: 'alice') }
|
||||||
|
|
||||||
|
describe 'GET #show' do
|
||||||
|
it 'returns 200' do
|
||||||
|
get :show, username: alice.username
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns 200 with Atom' do
|
||||||
|
get :show, username: alice.username, format: 'atom'
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,7 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe Api::SalmonController, type: :controller do
|
||||||
|
describe 'POST #update' do
|
||||||
|
pending
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,11 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe Api::SubscriptionsController, type: :controller do
|
||||||
|
describe 'GET #show' do
|
||||||
|
pending
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'POST #update' do
|
||||||
|
pending
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,11 +0,0 @@
|
||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
RSpec.describe AtomController, type: :controller do
|
|
||||||
describe 'GET #user_stream' do
|
|
||||||
pending
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'GET #entry' do
|
|
||||||
pending
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -2,6 +2,9 @@ require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe HomeController, type: :controller do
|
RSpec.describe HomeController, type: :controller do
|
||||||
describe 'GET #index' do
|
describe 'GET #index' do
|
||||||
pending
|
it 'returns 200' do
|
||||||
|
get :index
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
RSpec.describe ProfileController, type: :controller do
|
|
||||||
describe 'GET #show' do
|
|
||||||
pending
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'GET #entry' do
|
|
||||||
pending
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe StreamEntriesController, type: :controller do
|
||||||
|
let(:alice) { Fabricate(:account, username: 'alice') }
|
||||||
|
let(:status) { Fabricate(:status, account: alice) }
|
||||||
|
|
||||||
|
describe 'GET #show' do
|
||||||
|
it 'returns 200 with HTML' do
|
||||||
|
get :show, account_username: alice.username, id: status.stream_entry.id
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns 200 with Atom' do
|
||||||
|
get :show, account_username: alice.username, id: status.stream_entry.id, format: 'atom'
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -2,10 +2,23 @@ require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe XrdController, type: :controller do
|
RSpec.describe XrdController, type: :controller do
|
||||||
describe 'GET #host_meta' do
|
describe 'GET #host_meta' do
|
||||||
pending
|
it 'returns 200' do
|
||||||
|
get :host_meta
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'GET #webfinger' do
|
describe 'GET #webfinger' do
|
||||||
pending
|
let(:alice) { Fabricate(:account, username: 'alice') }
|
||||||
|
|
||||||
|
it 'returns 200 when account can be found' do
|
||||||
|
get :webfinger, resource: "acct:#{alice.username}@anything.com"
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns 404 when account cannot be found' do
|
||||||
|
get :webfinger, resource: 'acct:not@existing.com'
|
||||||
|
expect(response).to have_http_status(:not_found)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
# Specs in this file have access to a helper object that includes
|
||||||
|
# the AccountsHelper. For example:
|
||||||
|
#
|
||||||
|
# describe AccountsHelper do
|
||||||
|
# describe "string concat" do
|
||||||
|
# it "concats two strings with spaces" do
|
||||||
|
# expect(helper.concat_strings("this","that")).to eq("this that")
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
RSpec.describe AccountsHelper, type: :helper do
|
||||||
|
pending "add some examples to (or delete) #{__FILE__}"
|
||||||
|
end
|
|
@ -0,0 +1,15 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
# Specs in this file have access to a helper object that includes
|
||||||
|
# the Api::SalmonHelper. For example:
|
||||||
|
#
|
||||||
|
# describe Api::SalmonHelper do
|
||||||
|
# describe "string concat" do
|
||||||
|
# it "concats two strings with spaces" do
|
||||||
|
# expect(helper.concat_strings("this","that")).to eq("this that")
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
RSpec.describe Api::SalmonHelper, type: :helper do
|
||||||
|
pending "add some examples to (or delete) #{__FILE__}"
|
||||||
|
end
|
|
@ -0,0 +1,15 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
# Specs in this file have access to a helper object that includes
|
||||||
|
# the Api::SubscriptionsHelper. For example:
|
||||||
|
#
|
||||||
|
# describe Api::SubscriptionsHelper do
|
||||||
|
# describe "string concat" do
|
||||||
|
# it "concats two strings with spaces" do
|
||||||
|
# expect(helper.concat_strings("this","that")).to eq("this that")
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
RSpec.describe Api::SubscriptionsHelper, type: :helper do
|
||||||
|
pending "add some examples to (or delete) #{__FILE__}"
|
||||||
|
end
|
|
@ -28,18 +28,4 @@ RSpec.describe ApplicationHelper, type: :helper do
|
||||||
expect(helper.local_id?('tag:foreign.tld;objectId=12:objectType=Status')).to be false
|
expect(helper.local_id?('tag:foreign.tld;objectId=12:objectType=Status')).to be false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#add_base_url_prefix' do
|
|
||||||
it 'returns full API URL from base to suffix' do
|
|
||||||
expect(helper.add_base_url_prefix('test')).to eql "#{root_url}api/test"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#profile_url' do
|
|
||||||
pending
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#status_url' do
|
|
||||||
pending
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe AtomHelper, type: :helper do
|
RSpec.describe AtomBuilderHelper, type: :helper do
|
||||||
describe '#stream_updated_at' do
|
describe '#stream_updated_at' do
|
||||||
pending
|
pending
|
||||||
end
|
end
|
|
@ -1,6 +1,6 @@
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe ProfileHelper, type: :helper do
|
RSpec.describe StreamEntriesHelper, type: :helper do
|
||||||
describe '#display_name' do
|
describe '#display_name' do
|
||||||
pending
|
pending
|
||||||
end
|
end
|
Loading…
Reference in New Issue