rails 5 + api v2 + raml api docs (#593)

* update Gemfile to rails 5 and ruby 2.3.0

* fiddle with javascripts and add sprockets manifest file

* update config directory for rails 5

* fix some errors with controllers/serializers

* fix travis and rspec

* new serializers renamed to serializers

* module Api::V1

* reusable embedding code

* add index/collections/paging. overriding most of snorlax now |:)

* raml api documentation + rspec tests to verify schemas/examples

* add sorting by ?sort and searching by ?q. Add pagination Link headers

* api v1 => v2

* fill out synapse api

* alphabetize map policy

* fix page thing

* fill out maps api

* formParameters => properties, and fiddle with map api

* more raml 1.0 stuff i'm learning about

* deprecate v1 api

* rails 5 uses ApplicationRecord class for app-wide model config

* Update topic spec for api v2

* workaround for user_preference.rb issue

* get ready for token api docs. also TODO is mapping api docs

* spec out mapping api

* map/mapping/synapse spec, plus other bugs

* awesome, token specs/apis are done

* add sanity checks to the api tests

* more cleanup

* devise fix

* fix starred map error
This commit is contained in:
Devin Howard 2016-09-22 01:22:40 +08:00 committed by Connor Turland
parent aace6796f5
commit 3843cab643
148 changed files with 2506 additions and 694 deletions

20
Gemfile
View file

@ -1,28 +1,27 @@
source 'https://rubygems.org'
ruby '2.3.0'
gem 'rails'
gem 'rails', '~> 5.0.0'
gem 'active_model_serializers', '~> 0.8.1'
gem 'active_model_serializers'
gem 'aws-sdk', '< 2.0'
gem 'best_in_place' # in-place editing
gem 'delayed_job', '~> 4.0.2'
gem 'delayed_job_active_record', '~> 4.0.1'
gem 'best_in_place'
gem 'delayed_job'
gem 'delayed_job_active_record'
gem 'devise'
gem 'doorkeeper'
gem 'doorkeeper', '~> 4.0.0.rc4'
gem 'dotenv-rails'
gem 'exception_notification'
gem 'formtastic'
gem 'formula'
gem 'httparty'
gem 'json'
gem 'kaminari' # pagination
gem 'paperclip'
gem 'kaminari'
gem 'paperclip', '~> 4.3.6'
gem 'pg'
gem 'pundit'
gem 'pundit_extra'
gem 'rack-cors'
gem 'rails3-jquery-autocomplete'
gem 'redis'
gem 'slack-notifier'
gem 'snorlax'
@ -31,12 +30,12 @@ gem 'uservoice-ruby'
gem 'jquery-rails'
gem 'jquery-ui-rails'
gem 'jbuilder'
gem 'rails3-jquery-autocomplete'
group :assets do
gem 'coffee-rails'
gem 'sass-rails'
gem 'uglifier'
# gem 'therubyracer'
end
group :production do
@ -57,7 +56,6 @@ group :development, :test do
gem 'binding_of_caller'
gem 'pry-byebug'
gem 'pry-rails'
gem 'quiet_assets'
gem 'tunemygc'
gem 'rubocop'
end

View file

@ -1,45 +1,50 @@
GEM
remote: https://rubygems.org/
specs:
actionmailer (4.2.6)
actionpack (= 4.2.6)
actionview (= 4.2.6)
activejob (= 4.2.6)
actioncable (5.0.0)
actionpack (= 5.0.0)
nio4r (~> 1.2)
websocket-driver (~> 0.6.1)
actionmailer (5.0.0)
actionpack (= 5.0.0)
actionview (= 5.0.0)
activejob (= 5.0.0)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 1.0, >= 1.0.5)
actionpack (4.2.6)
actionview (= 4.2.6)
activesupport (= 4.2.6)
rack (~> 1.6)
rack-test (~> 0.6.2)
rails-dom-testing (~> 1.0, >= 1.0.5)
rails-dom-testing (~> 2.0)
actionpack (5.0.0)
actionview (= 5.0.0)
activesupport (= 5.0.0)
rack (~> 2.0)
rack-test (~> 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (4.2.6)
activesupport (= 4.2.6)
actionview (5.0.0)
activesupport (= 5.0.0)
builder (~> 3.1)
erubis (~> 2.7.0)
rails-dom-testing (~> 1.0, >= 1.0.5)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
active_model_serializers (0.8.3)
activemodel (>= 3.0)
activejob (4.2.6)
activesupport (= 4.2.6)
globalid (>= 0.3.0)
activemodel (4.2.6)
activesupport (= 4.2.6)
builder (~> 3.1)
activerecord (4.2.6)
activemodel (= 4.2.6)
activesupport (= 4.2.6)
arel (~> 6.0)
activesupport (4.2.6)
active_model_serializers (0.10.1)
actionpack (>= 4.1, < 6)
activemodel (>= 4.1, < 6)
jsonapi (~> 0.1.1.beta2)
railties (>= 4.1, < 6)
activejob (5.0.0)
activesupport (= 5.0.0)
globalid (>= 0.3.6)
activemodel (5.0.0)
activesupport (= 5.0.0)
activerecord (5.0.0)
activemodel (= 5.0.0)
activesupport (= 5.0.0)
arel (~> 7.0)
activesupport (5.0.0)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (~> 0.7)
json (~> 1.7, >= 1.7.7)
minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
addressable (2.3.8)
arel (6.0.3)
arel (7.1.1)
ast (2.3.0)
aws-sdk (1.66.0)
aws-sdk-v1 (= 1.66.0)
@ -56,7 +61,7 @@ GEM
rack (>= 0.9.0)
binding_of_caller (0.7.2)
debug_inspector (>= 0.0.1)
brakeman (3.3.2)
brakeman (3.3.3)
builder (3.2.2)
byebug (9.0.5)
climate_control (0.0.3)
@ -64,21 +69,21 @@ GEM
cocaine (0.5.8)
climate_control (>= 0.0.3, < 1.0)
coderay (1.1.1)
coffee-rails (4.1.1)
coffee-rails (4.2.1)
coffee-script (>= 2.2.0)
railties (>= 4.0.0, < 5.1.x)
railties (>= 4.0.0, < 5.2.x)
coffee-script (2.4.1)
coffee-script-source
execjs
coffee-script-source (1.10.0)
concurrent-ruby (1.0.2)
debug_inspector (0.0.2)
delayed_job (4.0.6)
activesupport (>= 3.0, < 5.0)
delayed_job_active_record (4.0.3)
activerecord (>= 3.0, < 5.0)
delayed_job (>= 3.0, < 4.1)
devise (4.1.1)
delayed_job (4.1.2)
activesupport (>= 3.0, < 5.1)
delayed_job_active_record (4.1.1)
activerecord (>= 3.0, < 5.1)
delayed_job (>= 3.0, < 5)
devise (4.2.0)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 4.1.0, < 5.1)
@ -86,16 +91,16 @@ GEM
warden (~> 1.2.3)
diff-lcs (1.2.5)
docile (1.1.5)
doorkeeper (3.1.0)
railties (>= 3.2)
doorkeeper (4.0.0)
railties (>= 4.2)
dotenv (2.1.1)
dotenv-rails (2.1.1)
dotenv (= 2.1.1)
railties (>= 4.0, < 5.1)
erubis (2.7.0)
exception_notification (4.1.4)
actionmailer (~> 4.0)
activesupport (~> 4.0)
exception_notification (4.2.1)
actionmailer (>= 4.0, < 6)
activesupport (>= 4.0, < 6)
execjs (2.7.0)
ezcrypto (0.7.2)
factory_girl (4.7.0)
@ -107,13 +112,12 @@ GEM
actionpack (>= 3.2.13)
formula (1.1.1)
rails (> 3.0.0)
globalid (0.3.6)
globalid (0.3.7)
activesupport (>= 4.1.0)
httparty (0.13.7)
json (~> 1.8)
httparty (0.14.0)
multi_xml (>= 0.5.2)
i18n (0.7.0)
jbuilder (2.5.0)
jbuilder (2.6.0)
activesupport (>= 3.0.0, < 5.1)
multi_json (~> 1.2)
jquery-rails (4.1.1)
@ -125,6 +129,8 @@ GEM
json (1.8.3)
json-schema (2.6.2)
addressable (~> 2.3.8)
jsonapi (0.1.1.beta2)
json (~> 1.8)
kaminari (0.17.0)
actionpack (>= 3.0.0)
activesupport (>= 3.0.0)
@ -141,6 +147,7 @@ GEM
minitest (5.9.0)
multi_json (1.12.1)
multi_xml (0.5.5)
nio4r (1.2.1)
nokogiri (1.6.8)
mini_portile2 (~> 2.1.0)
pkg-config (~> 1.1.7)
@ -157,7 +164,7 @@ GEM
pg (0.18.4)
pkg-config (1.1.7)
powerpack (0.1.1)
pry (0.10.3)
pry (0.10.4)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
slop (~> 3.4)
@ -169,29 +176,25 @@ GEM
pundit (1.1.0)
activesupport (>= 3.0.0)
pundit_extra (0.2.0)
quiet_assets (1.1.0)
railties (>= 3.1, < 5.0)
rack (1.6.4)
rack (2.0.1)
rack-cors (0.4.0)
rack-test (0.6.3)
rack (>= 1.0)
rails (4.2.6)
actionmailer (= 4.2.6)
actionpack (= 4.2.6)
actionview (= 4.2.6)
activejob (= 4.2.6)
activemodel (= 4.2.6)
activerecord (= 4.2.6)
activesupport (= 4.2.6)
rails (5.0.0)
actioncable (= 5.0.0)
actionmailer (= 5.0.0)
actionpack (= 5.0.0)
actionview (= 5.0.0)
activejob (= 5.0.0)
activemodel (= 5.0.0)
activerecord (= 5.0.0)
activesupport (= 5.0.0)
bundler (>= 1.3.0, < 2.0)
railties (= 4.2.6)
sprockets-rails
rails-deprecated_sanitizer (1.0.3)
activesupport (>= 4.2.0.alpha)
rails-dom-testing (1.0.7)
activesupport (>= 4.2.0.beta, < 5.0)
railties (= 5.0.0)
sprockets-rails (>= 2.0.0)
rails-dom-testing (2.0.1)
activesupport (>= 4.2.0, < 6.0)
nokogiri (~> 1.6.0)
rails-deprecated_sanitizer (>= 1.0.1)
rails-html-sanitizer (1.0.3)
loofah (~> 2.0)
rails3-jquery-autocomplete (1.0.15)
@ -201,34 +204,35 @@ GEM
rails_stdout_logging
rails_serve_static_assets (0.0.5)
rails_stdout_logging (0.0.5)
railties (4.2.6)
actionpack (= 4.2.6)
activesupport (= 4.2.6)
railties (5.0.0)
actionpack (= 5.0.0)
activesupport (= 5.0.0)
method_source
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
rainbow (2.1.0)
rake (11.2.2)
redis (3.3.0)
redis (3.3.1)
responders (2.2.0)
railties (>= 4.2.0, < 5.1)
rspec-core (3.4.4)
rspec-support (~> 3.4.0)
rspec-expectations (3.4.0)
rspec-core (3.5.2)
rspec-support (~> 3.5.0)
rspec-expectations (3.5.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.4.0)
rspec-mocks (3.4.1)
rspec-support (~> 3.5.0)
rspec-mocks (3.5.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.4.0)
rspec-rails (3.4.2)
actionpack (>= 3.0, < 4.3)
activesupport (>= 3.0, < 4.3)
railties (>= 3.0, < 4.3)
rspec-core (~> 3.4.0)
rspec-expectations (~> 3.4.0)
rspec-mocks (~> 3.4.0)
rspec-support (~> 3.4.0)
rspec-support (3.4.1)
rubocop (0.41.1)
rspec-support (~> 3.5.0)
rspec-rails (3.5.1)
actionpack (>= 3.0)
activesupport (>= 3.0)
railties (>= 3.0)
rspec-core (~> 3.5.0)
rspec-expectations (~> 3.5.0)
rspec-mocks (~> 3.5.0)
rspec-support (~> 3.5.0)
rspec-support (3.5.0)
rubocop (0.42.0)
parser (>= 2.3.1.1, < 3.0)
powerpack (~> 0.1)
rainbow (>= 1.99.1, < 3.0)
@ -236,8 +240,8 @@ GEM
unicode-display_width (~> 1.0, >= 1.0.1)
ruby-progressbar (1.8.1)
sass (3.4.22)
sass-rails (5.0.4)
railties (>= 4.0.0, < 5.0)
sass-rails (5.0.5)
railties (>= 4.0.0, < 6)
sass (~> 3.1)
sprockets (>= 2.8, < 4.0)
sprockets-rails (>= 2.0, < 4.0)
@ -253,20 +257,20 @@ GEM
slop (3.6.0)
snorlax (0.1.6)
rails (> 4.1)
sprockets (3.6.0)
sprockets (3.6.2)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (3.0.4)
sprockets-rails (3.1.1)
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets (>= 3.0.0)
thor (0.19.1)
thread_safe (0.3.5)
tilt (2.0.5)
tunemygc (1.0.65)
tunemygc (1.0.68)
tzinfo (1.2.2)
thread_safe (~> 0.1)
uglifier (3.0.0)
uglifier (3.0.1)
execjs (>= 0.3.0, < 3)
unicode-display_width (1.1.0)
uservoice-ruby (0.0.11)
@ -275,22 +279,25 @@ GEM
oauth (>= 0.4.7)
warden (1.2.6)
rack (>= 1.0)
websocket-driver (0.6.4)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.2)
PLATFORMS
ruby
DEPENDENCIES
active_model_serializers (~> 0.8.1)
active_model_serializers
aws-sdk (< 2.0)
best_in_place
better_errors
binding_of_caller
brakeman
coffee-rails
delayed_job (~> 4.0.2)
delayed_job_active_record (~> 4.0.1)
delayed_job
delayed_job_active_record
devise
doorkeeper
doorkeeper (~> 4.0.0.rc4)
dotenv-rails
exception_notification
factory_girl_rails
@ -303,15 +310,14 @@ DEPENDENCIES
json
json-schema
kaminari
paperclip
paperclip (~> 4.3.6)
pg
pry-byebug
pry-rails
pundit
pundit_extra
quiet_assets
rack-cors
rails
rails (~> 5.0.0)
rails3-jquery-autocomplete
rails_12factor
redis
@ -326,5 +332,8 @@ DEPENDENCIES
uglifier
uservoice-ruby
RUBY VERSION
ruby 2.3.0p0
BUNDLED WITH
1.11.2
1.12.5

View file

@ -0,0 +1,6 @@
// JS and CSS bundles
//= link_directory ../javascripts .js
//= link_directory ../stylesheets .css
// Other
//= link_tree ../images

View file

@ -1,2 +0,0 @@
class Api::MappingsController < API::RestfulController
end

View file

@ -1,2 +0,0 @@
class Api::MapsController < API::RestfulController
end

View file

@ -1,50 +0,0 @@
class API::RestfulController < ActionController::Base
include Pundit
include PunditExtra
snorlax_used_rest!
load_and_authorize_resource only: [:show, :update, :destroy]
def create
instantiate_resource
resource.user = current_user
authorize resource
create_action
respond_with_resource
end
private
def resource_serializer
"new_#{resource_name}_serializer".camelize.constantize
end
def accessible_records
if current_user
visible_records
else
public_records
end
end
def current_user
super || token_user || doorkeeper_user || nil
end
def token_user
token = params[:access_token]
access_token = Token.find_by_token(token)
@token_user ||= access_token.user if access_token
end
def doorkeeper_user
return unless doorkeeper_token.present?
doorkeeper_render_error unless valid_doorkeeper_token?
@doorkeeper_user ||= User.find(doorkeeper_token.resource_owner_id)
end
def permitted_params
@permitted_params ||= PermittedParams.new(params)
end
end

View file

@ -1,2 +0,0 @@
class Api::SynapsesController < API::RestfulController
end

View file

@ -1,17 +0,0 @@
class Api::TokensController < API::RestfulController
def my_tokens
raise Pundit::NotAuthorizedError unless current_user
instantiate_collection page_collection: false, timeframe_collection: false
respond_with_collection
end
private
def resource_serializer
"#{resource_name}_serializer".camelize.constantize
end
def visible_records
current_user.tokens
end
end

View file

@ -1,2 +0,0 @@
class Api::TopicsController < API::RestfulController
end

View file

@ -0,0 +1,9 @@
module Api
module V1
class DeprecatedController < ApplicationController
def method_missing
render json: { error: "/api/v1 is deprecated! Please use /api/v2 instead." }
end
end
end
end

View file

@ -0,0 +1,6 @@
module Api
module V1
class MappingsController < DeprecatedController
end
end
end

View file

@ -0,0 +1,6 @@
module Api
module V1
class MapsController < DeprecatedController
end
end
end

View file

@ -0,0 +1,6 @@
module Api
module V1
class SynapsesController < DeprecatedController
end
end
end

View file

@ -0,0 +1,6 @@
module Api
module V1
class TokensController < DeprecatedController
end
end
end

View file

@ -0,0 +1,6 @@
module Api
module V1
class TopicsController < DeprecatedController
end
end
end

View file

@ -0,0 +1,6 @@
module Api
module V2
class MappingsController < RestfulController
end
end
end

View file

@ -0,0 +1,9 @@
module Api
module V2
class MapsController < RestfulController
def searchable_columns
[:name, :desc]
end
end
end
end

View file

@ -0,0 +1,179 @@
module Api
module V2
class RestfulController < ActionController::Base
include Pundit
include PunditExtra
snorlax_used_rest!
before_action :load_resource, only: [:show, :update, :destroy]
after_action :verify_authorized
def index
authorize resource_class
instantiate_collection
respond_with_collection
end
def create
instantiate_resource
resource.user = current_user if current_user.present?
authorize resource
create_action
respond_with_resource
end
def destroy
destroy_action
head :no_content
end
private
def accessible_records
if current_user
visible_records
else
public_records
end
end
def current_user
super || token_user || doorkeeper_user || nil
end
def load_resource
super
authorize resource
end
def resource_serializer
"Api::V2::#{resource_name.camelize}Serializer".constantize
end
def respond_with_resource(scope: default_scope, serializer: resource_serializer, root: serializer_root)
if resource.errors.empty?
render json: resource, scope: scope, serializer: serializer, root: root
else
respond_with_errors
end
end
def respond_with_collection(resources: collection, scope: default_scope, serializer: resource_serializer, root: serializer_root)
render json: resources, scope: scope, each_serializer: serializer, root: root, meta: pagination(resources), meta_key: :page
end
def default_scope
{
embeds: embeds
}
end
def embeds
(params[:embed] || '').split(',').map(&:to_sym)
end
def token_user
token = params[:access_token]
access_token = Token.find_by_token(token)
@token_user ||= access_token.user if access_token
end
def doorkeeper_user
return unless doorkeeper_token.present?
doorkeeper_render_error unless valid_doorkeeper_token?
@doorkeeper_user ||= User.find(doorkeeper_token.resource_owner_id)
end
def permitted_params
@permitted_params ||= PermittedParams.new(params)
end
def serializer_root
'data'
end
def pagination(collection)
per = (params[:per] || 25).to_i
current_page = (params[:page] || 1).to_i
total_pages = (collection.total_count.to_f / per).ceil
prev_page = current_page > 1 ? current_page - 1 : 0
next_page = current_page < total_pages ? current_page + 1 : 0
base_url = request.base_url + request.path
nxt = request.query_parameters.merge(page: next_page).map{|x| x.join('=')}.join('&')
prev = request.query_parameters.merge(page: prev_page).map{|x| x.join('=')}.join('&')
last = request.query_parameters.merge(page: total_pages).map{|x| x.join('=')}.join('&')
response.headers['Link'] = [
%(<#{base_url}?#{nxt}>; rel="next"),
%(<#{base_url}?#{prev}>; rel="prev"),
%(<#{base_url}?#{last}>; rel="last")
].join(',')
response.headers['X-Total-Pages'] = collection.total_pages.to_s
response.headers['X-Total-Count'] = collection.total_count.to_s
response.headers['X-Per-Page'] = per.to_s
{
current_page: current_page,
next_page: next_page,
prev_page: prev_page,
total_pages: total_pages,
total_count: collection.total_count,
per: per
}
end
def instantiate_collection
collection = accessible_records
collection = yield collection if block_given?
collection = search_by_q(collection) if params[:q]
collection = order_by_sort(collection) if params[:sort]
collection = collection.page(params[:page]).per(params[:per])
self.collection = collection
end
# override this method to explicitly set searchable columns
def searchable_columns
columns = resource_class.columns.select do |column|
column.type == :text || column.type == :string
end
columns.map(&:name)
end
# thanks to http://stackoverflow.com/questions/4430578
def search_by_q(collection)
table = resource_class.arel_table
safe_query = "%#{params[:q].gsub(/[%_]/, '\\\\\0')}%"
search_column = -> (column) { table[column].matches(safe_query) }
condition = searchable_columns.reduce(nil) do |prev, column|
next search_column.(column) if prev.nil?
search_column.(column).or(prev)
end
puts collection.where(condition).to_sql
collection.where(condition)
end
def order_by_sort(collection)
builder = collection
sorts = params[:sort].split(',')
sorts.each do |sort|
direction = sort.starts_with?('-') ? 'desc' : 'asc'
sort = sort.sub(/^-/, '')
if resource_class.columns.map(&:name).include?(sort)
builder = builder.order(sort => direction)
end
end
return builder
end
def visible_records
policy_scope(resource_class)
end
def public_records
policy_scope(resource_class)
end
end
end
end

View file

@ -0,0 +1,20 @@
module Api
module V2
class SessionsController < ApplicationController
def create
@user = User.find_by(email: params[:email])
if @user && @user.valid_password(params[:password])
sign_in(@user)
render json: @user
else
render json: { error: 'Error' }
end
end
def destroy
sign_out
head :no_content
end
end
end
end

View file

@ -0,0 +1,9 @@
module Api
module V2
class SynapsesController < RestfulController
def searchable_columns
[:desc]
end
end
end
end

View file

@ -0,0 +1,11 @@
module Api
module V2
class TokensController < RestfulController
def my_tokens
authorize resource_class
instantiate_collection
respond_with_collection
end
end
end
end

View file

@ -0,0 +1,6 @@
module Api
module V2
class TopicsController < RestfulController
end
end
end

View file

@ -163,8 +163,8 @@ class MainController < ApplicationController
@synapses = []
end
# limit to 5 results
@synapses = @synapses.slice(0, 5)
#limit to 5 results
@synapses = @synapses.to_a.slice(0,5)
render json: autocomplete_synapse_array_json(@synapses)
end

View file

@ -1,6 +1,6 @@
class MapsController < ApplicationController
before_action :require_user, only: [:create, :update, :access, :star, :unstar, :screenshot, :events, :destroy]
after_action :verify_authorized, except: [:activemaps, :featuredmaps, :mymaps, :sharedmaps, :starredmaps, :usermaps, :events]
after_action :verify_authorized, except: [:activemaps, :featuredmaps, :mymaps, :sharedmaps, :starredmaps, :usermaps]
after_action :verify_policy_scoped, only: [:activemaps, :featuredmaps, :mymaps, :sharedmaps, :starredmaps, :usermaps]
respond_to :html, :json, :csv

View file

@ -15,11 +15,10 @@ class Users::RegistrationsController < Devise::RegistrationsController
private
def configure_sign_up_params
devise_parameter_sanitizer.for(:sign_up) << [:name, :joinedwithcode]
devise_parameter_sanitizer.permit(:sign_up, keys: [:name, :joinedwithcode])
end
def configure_account_update_params
puts devise_parameter_sanitizer_for(:account_update)
devise_parameter_sanitizer.for(:account_update) << [:image]
devise_parameter_sanitizer.permit(:account_update, keys: [:image])
end
end

View file

@ -0,0 +1,3 @@
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
end

View file

@ -1,4 +1,4 @@
class Event < ActiveRecord::Base
class Event < ApplicationRecord
KINDS = %w(user_present_on_map conversation_started_on_map topic_added_to_map synapse_added_to_map).freeze
# has_many :notifications, dependent: :destroy

View file

@ -1,4 +1,4 @@
class InMetacodeSet < ActiveRecord::Base
class InMetacodeSet < ApplicationRecord
belongs_to :metacode, class_name: 'Metacode', foreign_key: 'metacode_id'
belongs_to :metacode_set, class_name: 'MetacodeSet', foreign_key: 'metacode_set_id'
end

View file

@ -1,4 +1,4 @@
class Map < ActiveRecord::Base
class Map < ApplicationRecord
belongs_to :user
has_many :topicmappings, -> { Mapping.topicmapping }, class_name: :Mapping, dependent: :destroy

View file

@ -1,4 +1,4 @@
class Mapping < ActiveRecord::Base
class Mapping < ApplicationRecord
scope :topicmapping, -> { where(mappable_type: :Topic) }
scope :synapsemapping, -> { where(mappable_type: :Synapse) }

View file

@ -1,4 +1,4 @@
class Message < ActiveRecord::Base
class Message < ApplicationRecord
belongs_to :user
belongs_to :resource, polymorphic: true

View file

@ -1,4 +1,4 @@
class Metacode < ActiveRecord::Base
class Metacode < ApplicationRecord
has_many :in_metacode_sets
has_many :metacode_sets, through: :in_metacode_sets
has_many :topics

View file

@ -1,4 +1,4 @@
class MetacodeSet < ActiveRecord::Base
class MetacodeSet < ApplicationRecord
belongs_to :user
has_many :in_metacode_sets
has_many :metacodes, through: :in_metacode_sets

View file

@ -1,4 +1,4 @@
class Synapse < ActiveRecord::Base
class Synapse < ApplicationRecord
belongs_to :user
belongs_to :defer_to_map, class_name: 'Map', foreign_key: 'defer_to_map_id'

View file

@ -1,4 +1,4 @@
class Token < ActiveRecord::Base
class Token < ApplicationRecord
belongs_to :user
before_create :assign_token

View file

@ -1,4 +1,4 @@
class Topic < ActiveRecord::Base
class Topic < ApplicationRecord
include TopicsHelper
belongs_to :user

View file

@ -1,6 +1,6 @@
require 'open-uri'
class User < ActiveRecord::Base
class User < ApplicationRecord
has_many :topics
has_many :synapses
has_many :maps
@ -80,7 +80,7 @@ class User < ActiveRecord::Base
end
def starred_map?(map)
return !!self.stars.index{|s| s.map_id == map.id }
return self.stars.where(map_id: map.id).exists?
end
def settings

View file

@ -1,4 +1,4 @@
class UserMap < ActiveRecord::Base
class UserMap < ApplicationRecord
belongs_to :map
belongs_to :user
end

View file

@ -4,8 +4,14 @@ class UserPreference
def initialize
array = []
%w(Action Aim Idea Question Note Wildcard Subject).each do |m|
metacode = Metacode.find_by_name(m)
array.push(metacode.id.to_s) if metacode
begin
metacode = Metacode.find_by_name(m)
array.push(metacode.id.to_s) if metacode
rescue ActiveRecord::StatementInvalid
if m == 'Action'
Rails.logger.warn("TODO: remove this travis workaround in user_preference.rb")
end
end
end
@metacodes = array
end

View file

@ -1,4 +1,4 @@
class Webhook < ActiveRecord::Base
class Webhook < ApplicationRecord
belongs_to :hookable, polymorphic: true
validates :uri, presence: true

View file

@ -12,19 +12,7 @@ class MapPolicy < ApplicationPolicy
end
end
def activemaps?
user.blank? # redirect to root url if authenticated for some reason
end
def featuredmaps?
true
end
def mymaps?
user.present?
end
def usermaps?
def index?
true
end
@ -32,18 +20,6 @@ class MapPolicy < ApplicationPolicy
record.permission == 'commons' || record.permission == 'public' || record.collaborators.include?(user) || record.user == user
end
def export?
show?
end
def events?
show?
end
def contains?
show?
end
def create?
user.present?
end
@ -52,11 +28,39 @@ class MapPolicy < ApplicationPolicy
user.present? && (record.permission == 'commons' || record.collaborators.include?(user) || record.user == user)
end
def destroy?
record.user == user || admin_override
end
def access?
# note that this is to edit access
user.present? && record.user == user
end
def activemaps?
user.blank? # redirect to root url if authenticated for some reason
end
def contains?
show?
end
def events?
show?
end
def export?
show?
end
def featuredmaps?
true
end
def mymaps?
user.present?
end
def star?
unstar?
end
@ -69,7 +73,7 @@ class MapPolicy < ApplicationPolicy
update?
end
def destroy?
record.user == user || admin_override
def usermaps?
true
end
end

View file

@ -8,13 +8,17 @@ class MappingPolicy < ApplicationPolicy
visible = %w(public commons)
permission = 'maps.permission IN (?)'
if user
scope.joins(:maps).where(permission + ' OR maps.user_id = ?', visible, user.id)
scope.joins(:map).where(permission, visible).or(scope.joins(:map).where(user_id: user.id))
else
scope.where(permission, visible)
scope.joins(:map).where(permission, visible)
end
end
end
def index?
true
end
def show?
map_policy.show? && mappable_policy.try(:show?)
end

View file

@ -11,6 +11,10 @@ class SynapsePolicy < ApplicationPolicy
end
end
def index?
true # really only for the API. should be policy scoped!
end
def create?
user.present?
# TODO: add validation against whether you can see both topics

View file

@ -11,6 +11,10 @@ class TopicPolicy < ApplicationPolicy
end
end
def index?
user.present?
end
def create?
user.present?
end

View file

@ -0,0 +1,29 @@
module Api
module V2
class ApplicationSerializer < ActiveModel::Serializer
def self.embeddable
{}
end
def embeds
@embeds ||= (scope[:embeds] || []).select { |e| self.class.embeddable.keys.include?(e) }
end
def self.embed_dat
embeddable.each_pair do |key, opts|
attr = opts.delete(:attr) || key
if attr.to_s.pluralize == attr.to_s
attribute "#{attr.to_s.singularize}_ids".to_sym, opts.merge(unless: -> { embeds.include?(key) }) do
object.send(attr).map(&:id)
end
has_many attr, opts.merge(if: -> { embeds.include?(key) })
else
id_opts = opts.merge(key: "#{key}_id")
attribute "#{attr}_id".to_sym, id_opts.merge(unless: -> { embeds.include?(key) })
attribute key, opts.merge(if: -> { embeds.include?(key) })
end
end
end
end
end
end

View file

@ -0,0 +1,18 @@
module Api
module V2
class EventSerializer < ApplicationSerializer
attributes :id, :sequence_id, :kind, :map_id, :created_at
has_one :actor, serializer: UserSerializer, root: 'users'
has_one :map, serializer: MapSerializer
def actor
object.user || object.eventable.try(:user)
end
def map
object.eventable.try(:map) || object.eventable.map
end
end
end
end

View file

@ -0,0 +1,28 @@
module Api
module V2
class MapSerializer < ApplicationSerializer
attributes :id,
:name,
:desc,
:permission,
:screenshot,
:created_at,
:updated_at
def self.embeddable
{
user: {},
topics: {},
synapses: {},
mappings: {},
contributors: { serializer: UserSerializer },
collaborators: { serializer: UserSerializer }
}
end
self.class_eval do
embed_dat
end
end
end
end

View file

@ -0,0 +1,25 @@
module Api
module V2
class MappingSerializer < ApplicationSerializer
attributes :id,
:created_at,
:updated_at,
:mappable_id,
:mappable_type
attribute :xloc, if: -> { object.mappable_type == 'Topic' }
attribute :yloc, if: -> { object.mappable_type == 'Topic' }
def self.embeddable
{
user: {},
map: {}
}
end
self.class_eval do
embed_dat
end
end
end
end

View file

@ -0,0 +1,11 @@
module Api
module V2
class MetacodeSerializer < ApplicationSerializer
attributes :id,
:name,
:manual_icon,
:color,
:aws_icon
end
end
end

View file

@ -0,0 +1,24 @@
module Api
module V2
class SynapseSerializer < ApplicationSerializer
attributes :id,
:desc,
:category,
:permission,
:created_at,
:updated_at
def self.embeddable
{
topic1: { attr: :node1, serializer: TopicSerializer },
topic2: { attr: :node2, serializer: TopicSerializer },
user: {}
}
end
self.class_eval do
embed_dat
end
end
end
end

View file

@ -0,0 +1,10 @@
module Api
module V2
class TokenSerializer < ApplicationSerializer
attributes :id,
:token,
:description,
:created_at
end
end
end

View file

@ -0,0 +1,24 @@
module Api
module V2
class TopicSerializer < ApplicationSerializer
attributes :id,
:name,
:desc,
:link,
:permission,
:created_at,
:updated_at
def self.embeddable
{
user: {},
metacode: {}
}
end
self.class_eval do
embed_dat
end
end
end
end

View file

@ -0,0 +1,19 @@
module Api
module V2
class UserSerializer < ApplicationSerializer
attributes :id,
:name,
:avatar,
:is_admin,
:generation
def avatar
object.image.url(:sixtyfour)
end
def is_admin
object.admin
end
end
end
end

View file

@ -0,0 +1,7 @@
module Api
module V2
class WebhookSerializer < ApplicationSerializer
attributes :text, :username, :icon_url # , :attachments
end
end
end

View file

@ -1,15 +0,0 @@
class EventSerializer < ActiveModel::Serializer
embed :ids, include: true
attributes :id, :sequence_id, :kind, :map_id, :created_at
has_one :actor, serializer: NewUserSerializer, root: 'users'
has_one :map, serializer: NewMapSerializer
def actor
object.user || object.eventable.try(:user)
end
def map
object.eventable.try(:map) || object.eventable.map
end
end

View file

@ -1,16 +0,0 @@
class NewMapSerializer < ActiveModel::Serializer
embed :ids, include: true
attributes :id,
:name,
:desc,
:permission,
:screenshot,
:created_at,
:updated_at
has_many :topics, serializer: NewTopicSerializer
has_many :synapses, serializer: NewSynapseSerializer
has_many :mappings, serializer: NewMappingSerializer
has_many :contributors, root: :users, serializer: NewUserSerializer
has_many :collaborators, root: :users, serializer: NewUserSerializer
end

View file

@ -1,19 +0,0 @@
class NewMappingSerializer < ActiveModel::Serializer
embed :ids, include: true
attributes :id,
:xloc,
:yloc,
:created_at,
:updated_at,
:mappable_id,
:mappable_type
has_one :user, serializer: NewUserSerializer
has_one :map, serializer: NewMapSerializer
def filter(keys)
keys.delete(:xloc) unless object.mappable_type == 'Topic'
keys.delete(:yloc) unless object.mappable_type == 'Topic'
keys
end
end

View file

@ -1,7 +0,0 @@
class NewMetacodeSerializer < ActiveModel::Serializer
attributes :id,
:name,
:manual_icon,
:color,
:aws_icon
end

View file

@ -1,14 +0,0 @@
class NewSynapseSerializer < ActiveModel::Serializer
embed :ids, include: true
attributes :id,
:desc,
:category,
:weight,
:permission,
:created_at,
:updated_at
has_one :topic1, root: :topics, serializer: NewTopicSerializer
has_one :topic2, root: :topics, serializer: NewTopicSerializer
has_one :user, serializer: NewUserSerializer
end

View file

@ -1,13 +0,0 @@
class NewTopicSerializer < ActiveModel::Serializer
embed :ids, include: true
attributes :id,
:name,
:desc,
:link,
:permission,
:created_at,
:updated_at
has_one :user, serializer: NewUserSerializer
has_one :metacode, serializer: NewMetacodeSerializer
end

View file

@ -1,15 +0,0 @@
class NewUserSerializer < ActiveModel::Serializer
attributes :id,
:name,
:avatar,
:is_admin,
:generation
def avatar
object.image.url(:sixtyfour)
end
def is_admin
object.admin
end
end

View file

@ -1,7 +0,0 @@
class TokenSerializer < ActiveModel::Serializer
attributes :id,
:token,
:description,
:created_at,
:updated_at
end

View file

@ -1,3 +0,0 @@
class WebhookSerializer < ActiveModel::Serializer
attributes :text, :username, :icon_url # , :attachments
end

View file

@ -1,4 +1,4 @@
require File.expand_path('../boot', __FILE__)
require_relative 'boot'
require 'csv'
require 'rails/all'
@ -15,21 +15,6 @@ module Metamaps
# Custom directories with classes and modules you want to be autoloadable.
config.autoload_paths << Rails.root.join('app', 'services')
# Only load the plugins named here, in the order given (default is alphabetical).
# :all can be used as a placeholder for all plugins not explicitly named.
# config.plugins = [ :exception_notification, :ssl_requirement, :all ]
# Activate observers that should always be running.
# config.active_record.observers = :cacher, :garbage_collector, :forum_observer
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
# config.time_zone = 'Central Time (US & Canada)'
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
# config.i18n.default_locale = :de
# Configure the default encoding used in templates for Ruby 1.9.
config.encoding = 'utf-8'
@ -43,11 +28,6 @@ module Metamaps
# Configure sensitive parameters which will be filtered from the log file.
config.filter_parameters += [:password]
# Use SQL instead of Active Record's schema dumper when creating the database.
# This is necessary if your schema can't be completely dumped by the schema dumper,
# like if you have constraints or database-specific column types
# config.active_record.schema_format = :sql
# Enable the asset pipeline
config.assets.initialize_on_precompile = false
@ -57,7 +37,6 @@ module Metamaps
config.generators do |g|
g.test_framework :rspec
end
config.active_record.raise_in_transactional_callbacks = true
# pundit errors return 403 FORBIDDEN
config.action_dispatch.rescue_responses['Pundit::NotAuthorizedError'] = :forbidden

View file

@ -1,5 +1,6 @@
require 'rubygems'
require 'rails/commands/server'
module Rails
class Server
def default_options
@ -9,6 +10,6 @@ module Rails
end
# Set up gems listed in the Gemfile.
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
require 'bundler/setup'

9
config/cable.yml Normal file
View file

@ -0,0 +1,9 @@
development:
adapter: async
test:
adapter: async
production:
adapter: redis
url: redis://localhost:6379/1

View file

@ -1,5 +1,5 @@
# Load the rails application
require File.expand_path('../application', __FILE__)
# Load the Rails application.
require_relative 'application'
# Initialize the rails application
Metamaps::Application.initialize!
# Initialize the Rails application.
Rails.application.initialize!

View file

@ -1,9 +1,8 @@
Metamaps::Application.configure do
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb
config.log_level = :warn
config.eager_load = true
config.assets.js_compressor = :uglifier
# Code is not reloaded between requests
config.cache_classes = true
@ -13,12 +12,12 @@ Metamaps::Application.configure do
config.action_controller.perform_caching = true
# Disable Rails's static asset server (Apache or nginx will already do this)
config.serve_static_files = true
config.public_file_server.enabled = false
# Don't fallback to assets pipeline if a precompiled asset is missed
config.assets.compile = false
# Compress JavaScripts and CSS
config.assets.compress = true
config.assets.js_compressor = :uglifier
# S3 file storage
config.paperclip_defaults = {
@ -37,7 +36,6 @@ Metamaps::Application.configure do
port: ENV['SMTP_PORT'],
user_name: ENV['SMTP_USERNAME'],
password: ENV['SMTP_PASSWORD'],
# domain: ENV['SMTP_DOMAIN']
authentication: 'plain',
enable_starttls_auto: true,
openssl_verify_mode: 'none'
@ -46,54 +44,13 @@ Metamaps::Application.configure do
# Don't care if the mailer can't send
config.action_mailer.raise_delivery_errors = true
# Don't fallback to assets pipeline if a precompiled asset is missed
config.assets.compile = false
# Generate digests for assets URLs
config.assets.digest = true
# Defaults to Rails.root.join("public/assets")
# config.assets.manifest = YOUR_PATH
# Specifies the header that your server uses for sending files
# config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
# config.force_ssl = true
# See everything in the log (default is :info)
# config.log_level = :debug
# Prepend all log lines with the following tags
# config.log_tags = [ :subdomain, :uuid ]
# Use a different logger for distributed setups
# config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
# Use a different cache store in production
# config.cache_store = :mem_cache_store
# Enable serving of images, stylesheets, and JavaScripts from an asset server
# config.action_controller.asset_host = "http://assets.example.com"
# Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
# config.assets.precompile += %w( )
# Disable delivery errors, bad email addresses will be ignored
# config.action_mailer.raise_delivery_errors = false
# Enable threaded mode
# config.threadsafe!
# Enable locale fallbacks for I18n (makes lookups for any locale fall back to
# the I18n.default_locale when a translation can not be found)
config.i18n.fallbacks = true
# Send deprecation notices to registered listeners
config.active_support.deprecation = :notify
# Log the query plan for queries taking more than this (works
# with SQLite, MySQL, and PostgreSQL)
# config.active_record.auto_explain_threshold_in_seconds = 0.5
end

View file

@ -10,8 +10,10 @@ Metamaps::Application.configure do
config.cache_classes = true
# Configure static asset server for tests with Cache-Control for performance
config.serve_static_files = true
config.static_cache_control = 'public, max-age=3600'
config.public_file_server.enabled = true
config.public_file_server.headers = {
'Cache-Control' => 'public, max-age=3600'
}
# Show full error reports and disable caching
config.consider_all_requests_local = true

View file

@ -1,4 +1,4 @@
$codes = []
if ActiveRecord::Base.connection.table_exists? 'users'
if ActiveRecord::Base.connection.data_source_exists? 'users'
$codes = ActiveRecord::Base.connection.execute('SELECT code FROM users').map { |user| user['code'] }
end

View file

@ -0,0 +1 @@
ActiveModelSerializers.config.adapter = :json

View file

@ -0,0 +1,6 @@
# Be sure to restart your server when you modify this file.
# ApplicationController.renderer.defaults.merge!(
# http_host: 'example.org',
# https: false
# )

View file

@ -1 +1,12 @@
# Be sure to restart your server when you modify this file.
# Version of your assets, change this if you want to expire all your assets.
Rails.application.config.assets.version = '2.0'
Rails.application.config.assets.quiet = true
# Add additional assets to the asset load path
# Rails.application.config.assets.paths << Emoji.images_path
# Precompile additional assets.
# application.js, application.css, and all non-JS/CSS in app/assets folder are already added.
Rails.application.config.assets.precompile += %w( webpacked/metamaps.bundle.js )

View file

@ -0,0 +1,5 @@
# Be sure to restart your server when you modify this file.
# Specify a serializer for the signed and encrypted cookie jars.
# Valid options are :json, :marshal, and :hybrid.
Rails.application.config.action_dispatch.cookies_serializer = :hybrid

View file

@ -0,0 +1,4 @@
# Be sure to restart your server when you modify this file.
# Configure sensitive parameters which will be filtered from the log file.
Rails.application.config.filter_parameters += [:password]

View file

@ -1,15 +1,16 @@
# Be sure to restart your server when you modify this file.
# Add new inflection rules using the following format
# (all these examples are active by default):
# ActiveSupport::Inflector.inflections do |inflect|
# Add new inflection rules using the following format. Inflections
# are locale specific, and you may define rules for as many different
# locales as you wish. All of these examples are active by default:
# ActiveSupport::Inflector.inflections(:en) do |inflect|
# inflect.plural /^(ox)$/i, '\1en'
# inflect.singular /^(ox)en/i, '\1'
# inflect.irregular 'person', 'people'
# inflect.uncountable %w( fish sheep )
# end
#
# These inflection rules are supported but not enabled by default:
# ActiveSupport::Inflector.inflections do |inflect|
# ActiveSupport::Inflector.inflections(:en) do |inflect|
# inflect.acronym 'RESTful'
# end

View file

@ -0,0 +1,10 @@
Kaminari.configure do |config|
# config.default_per_page = 25
# config.max_per_page = nil
# config.window = 4
# config.outer_window = 0
# config.left = 0
# config.right = 0
# config.page_method_name = :page
# config.param_name = :page
end

View file

@ -2,6 +2,5 @@
# Add new mime types for use in respond_to blocks:
# Mime::Type.register "text/richtext", :rtf
# Mime::Type.register_alias "text/html", :iphone
Mime::Type.register 'application/xls', :xls

View file

@ -0,0 +1,24 @@
# Be sure to restart your server when you modify this file.
#
# This file contains migration options to ease your Rails 5.0 upgrade.
#
# Read the Rails 5.0 release notes for more info on each option.
# Enable per-form CSRF tokens. Previous versions had false.
Rails.application.config.action_controller.per_form_csrf_tokens = true
# Enable origin-checking CSRF mitigation. Previous versions had false.
Rails.application.config.action_controller.forgery_protection_origin_check = true
# Make Ruby 2.4 preserve the timezone of the receiver when calling `to_time`.
# Previous versions had false.
ActiveSupport.to_time_preserves_timezone = true
# Require `belongs_to` associations by default. Previous versions had false.
Rails.application.config.active_record.belongs_to_required_by_default = true
# Do not halt callback chains when a callback returns false. Previous versions had true.
ActiveSupport.halt_callback_chains_on_return_false = false
# Configure SSL options to enable HSTS with subdomains. Previous versions had false.
Rails.application.config.ssl_options = { hsts: { subdomains: true } }

View file

@ -4,4 +4,4 @@
# If you change this key, all old signed cookies will become invalid!
# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
Metamaps::Application.config.secret_key_base = ENV['SECRET_KEY_BASE']
Rails.application.config.secret_key_base = ENV['SECRET_KEY_BASE']

View file

@ -1,8 +1,8 @@
# Be sure to restart your server when you modify this file.
Metamaps::Application.config.session_store :cookie_store, key: '_Metamaps_session'
Rails.application.config.session_store :cookie_store, key: '_Metamaps_session'
# Use the database for sessions instead of the cookie-based default,
# which shouldn't be used to store highly confidential information
# (create the session table with "rails generate session_migration")
# Metamaps::Application.config.session_store :active_record_store
# Rails.application.config.session_store :active_record_store

View file

@ -8,7 +8,7 @@ ActiveSupport.on_load(:action_controller) do
wrap_parameters format: [:json]
end
# Disable root element in JSON by default.
ActiveSupport.on_load(:active_record) do
self.include_root_in_json = false
end
# To enable root element in JSON for ActiveRecord objects.
# ActiveSupport.on_load(:active_record) do
# self.include_root_in_json = true
# end

47
config/puma.rb Normal file
View file

@ -0,0 +1,47 @@
# Puma can serve each request in a thread from an internal thread pool.
# The `threads` method setting takes two numbers a minimum and maximum.
# Any libraries that use thread pools should be configured to match
# the maximum value specified for Puma. Default is set to 5 threads for minimum
# and maximum, this matches the default thread size of Active Record.
#
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }.to_i
threads threads_count, threads_count
# Specifies the `port` that Puma will listen on to receive requests, default is 3000.
#
port ENV.fetch("PORT") { 3000 }
# Specifies the `environment` that Puma will run in.
#
environment ENV.fetch("RAILS_ENV") { "development" }
# Specifies the number of `workers` to boot in clustered mode.
# Workers are forked webserver processes. If using threads and workers together
# the concurrency of the application would be max `threads` * `workers`.
# Workers do not work on JRuby or Windows (both of which do not support
# processes).
#
# workers ENV.fetch("WEB_CONCURRENCY") { 2 }
# Use the `preload_app!` method when specifying a `workers` number.
# This directive tells Puma to first boot the application and load code
# before forking the application. This takes advantage of Copy On Write
# process behavior so workers use less memory. If you use this option
# you need to make sure to reconnect any threads in the `on_worker_boot`
# block.
#
# preload_app!
# The code in the `on_worker_boot` will be called if you are using
# clustered mode by specifying a number of `workers`. After each worker
# process is booted this block will be run, if you are using `preload_app!`
# option you will want to use this block to reconnect to any threads
# or connections that may have been created at application boot, Ruby
# cannot share connections between processes.
#
# on_worker_boot do
# ActiveRecord::Base.establish_connection if defined?(ActiveRecord)
# end
# Allow puma to be restarted by `rails restart` command.
plugin :tmp_restart

View file

@ -9,13 +9,26 @@ Metamaps::Application.routes.draw do
get 'search/mappers', to: 'main#searchmappers', as: :searchmappers
get 'search/synapses', to: 'main#searchsynapses', as: :searchsynapses
namespace :api, path: '/api/v1', defaults: { format: :json } do
resources :maps, only: [:create, :show, :update, :destroy]
resources :synapses, only: [:create, :show, :update, :destroy]
resources :topics, only: [:create, :show, :update, :destroy]
resources :mappings, only: [:create, :show, :update, :destroy]
resources :tokens, only: [:create, :destroy] do
get :my_tokens, on: :collection
namespace :api, path: '/api', default: { format: :json } do
namespace :v2, path: '/v2' do
resources :maps, only: [:index, :create, :show, :update, :destroy]
resources :synapses, only: [:index, :create, :show, :update, :destroy]
resources :topics, only: [:index, :create, :show, :update, :destroy]
resources :mappings, only: [:index, :create, :show, :update, :destroy]
resources :tokens, only: [:create, :destroy] do
get :my_tokens, on: :collection
end
end
namespace :v1, path: '/v1' do
# api v1 routes all lead to a deprecation error method
# see app/controllers/api/v1/deprecated_controller.rb
resources :maps, only: [:create, :show, :update, :destroy]
resources :synapses, only: [:create, :show, :update, :destroy]
resources :topics, only: [:create, :show, :update, :destroy]
resources :mappings, only: [:create, :show, :update, :destroy]
resources :tokens, only: [:create, :destroy] do
get :my_tokens, on: :collection
end
end
end

7
config/spring.rb Normal file
View file

@ -0,0 +1,7 @@
%w(
.ruby-version
.ruby-gemset
.rbenv-vars
tmp/restart.txt
tmp/caching-dev.txt
).each { |path| Spring.watch(path) }

View file

@ -1,4 +1,3 @@
# encoding: UTF-8
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
@ -28,10 +27,9 @@ ActiveRecord::Schema.define(version: 20160820231717) do
t.string "queue"
t.datetime "created_at"
t.datetime "updated_at"
t.index ["priority", "run_at"], name: "delayed_jobs_priority", using: :btree
end
add_index "delayed_jobs", ["priority", "run_at"], name: "delayed_jobs_priority", using: :btree
create_table "events", force: :cascade do |t|
t.string "kind", limit: 255
t.integer "eventable_id"
@ -41,24 +39,22 @@ ActiveRecord::Schema.define(version: 20160820231717) do
t.integer "sequence_id"
t.datetime "created_at"
t.datetime "updated_at"
t.index ["eventable_type", "eventable_id"], name: "index_events_on_eventable_type_and_eventable_id", using: :btree
t.index ["map_id", "sequence_id"], name: "index_events_on_map_id_and_sequence_id", unique: true, using: :btree
t.index ["map_id"], name: "index_events_on_map_id", using: :btree
t.index ["sequence_id"], name: "index_events_on_sequence_id", using: :btree
t.index ["user_id"], name: "index_events_on_user_id", using: :btree
end
add_index "events", ["eventable_type", "eventable_id"], name: "index_events_on_eventable_type_and_eventable_id", using: :btree
add_index "events", ["map_id", "sequence_id"], name: "index_events_on_map_id_and_sequence_id", unique: true, using: :btree
add_index "events", ["map_id"], name: "index_events_on_map_id", using: :btree
add_index "events", ["sequence_id"], name: "index_events_on_sequence_id", using: :btree
add_index "events", ["user_id"], name: "index_events_on_user_id", using: :btree
create_table "in_metacode_sets", force: :cascade do |t|
t.integer "metacode_id"
t.integer "metacode_set_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["metacode_id"], name: "index_in_metacode_sets_on_metacode_id", using: :btree
t.index ["metacode_set_id"], name: "index_in_metacode_sets_on_metacode_set_id", using: :btree
end
add_index "in_metacode_sets", ["metacode_id"], name: "index_in_metacode_sets_on_metacode_id", using: :btree
add_index "in_metacode_sets", ["metacode_set_id"], name: "index_in_metacode_sets_on_metacode_set_id", using: :btree
create_table "mappings", force: :cascade do |t|
t.text "category"
t.integer "xloc"
@ -71,14 +67,13 @@ ActiveRecord::Schema.define(version: 20160820231717) do
t.datetime "updated_at", null: false
t.integer "mappable_id"
t.string "mappable_type"
t.index ["map_id", "synapse_id"], name: "index_mappings_on_map_id_and_synapse_id", using: :btree
t.index ["map_id", "topic_id"], name: "index_mappings_on_map_id_and_topic_id", using: :btree
t.index ["map_id"], name: "index_mappings_on_map_id", using: :btree
t.index ["mappable_id", "mappable_type"], name: "index_mappings_on_mappable_id_and_mappable_type", using: :btree
t.index ["user_id"], name: "index_mappings_on_user_id", using: :btree
end
add_index "mappings", ["map_id", "synapse_id"], name: "index_mappings_on_map_id_and_synapse_id", using: :btree
add_index "mappings", ["map_id", "topic_id"], name: "index_mappings_on_map_id_and_topic_id", using: :btree
add_index "mappings", ["map_id"], name: "index_mappings_on_map_id", using: :btree
add_index "mappings", ["mappable_id", "mappable_type"], name: "index_mappings_on_mappable_id_and_mappable_type", using: :btree
add_index "mappings", ["user_id"], name: "index_mappings_on_user_id", using: :btree
create_table "maps", force: :cascade do |t|
t.text "name"
t.boolean "arranged"
@ -92,10 +87,9 @@ ActiveRecord::Schema.define(version: 20160820231717) do
t.string "screenshot_content_type"
t.integer "screenshot_file_size"
t.datetime "screenshot_updated_at"
t.index ["user_id"], name: "index_maps_on_user_id", using: :btree
end
add_index "maps", ["user_id"], name: "index_maps_on_user_id", using: :btree
create_table "messages", force: :cascade do |t|
t.text "message"
t.integer "user_id"
@ -103,12 +97,11 @@ ActiveRecord::Schema.define(version: 20160820231717) do
t.string "resource_type"
t.datetime "created_at"
t.datetime "updated_at"
t.index ["resource_id"], name: "index_messages_on_resource_id", using: :btree
t.index ["resource_type"], name: "index_messages_on_resource_type", using: :btree
t.index ["user_id"], name: "index_messages_on_user_id", using: :btree
end
add_index "messages", ["resource_id"], name: "index_messages_on_resource_id", using: :btree
add_index "messages", ["resource_type"], name: "index_messages_on_resource_type", using: :btree
add_index "messages", ["user_id"], name: "index_messages_on_user_id", using: :btree
create_table "metacode_sets", force: :cascade do |t|
t.string "name"
t.text "desc"
@ -116,10 +109,9 @@ ActiveRecord::Schema.define(version: 20160820231717) do
t.boolean "mapperContributed"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["user_id"], name: "index_metacode_sets_on_user_id", using: :btree
end
add_index "metacode_sets", ["user_id"], name: "index_metacode_sets_on_user_id", using: :btree
create_table "metacodes", force: :cascade do |t|
t.text "name"
t.string "manual_icon"
@ -141,10 +133,9 @@ ActiveRecord::Schema.define(version: 20160820231717) do
t.datetime "created_at", null: false
t.datetime "revoked_at"
t.string "scopes"
t.index ["token"], name: "index_oauth_access_grants_on_token", unique: true, using: :btree
end
add_index "oauth_access_grants", ["token"], name: "index_oauth_access_grants_on_token", unique: true, using: :btree
create_table "oauth_access_tokens", force: :cascade do |t|
t.integer "resource_owner_id"
t.integer "application_id"
@ -154,12 +145,11 @@ ActiveRecord::Schema.define(version: 20160820231717) do
t.datetime "revoked_at"
t.datetime "created_at", null: false
t.string "scopes"
t.index ["refresh_token"], name: "index_oauth_access_tokens_on_refresh_token", unique: true, using: :btree
t.index ["resource_owner_id"], name: "index_oauth_access_tokens_on_resource_owner_id", using: :btree
t.index ["token"], name: "index_oauth_access_tokens_on_token", unique: true, using: :btree
end
add_index "oauth_access_tokens", ["refresh_token"], name: "index_oauth_access_tokens_on_refresh_token", unique: true, using: :btree
add_index "oauth_access_tokens", ["resource_owner_id"], name: "index_oauth_access_tokens_on_resource_owner_id", using: :btree
add_index "oauth_access_tokens", ["token"], name: "index_oauth_access_tokens_on_token", unique: true, using: :btree
create_table "oauth_applications", force: :cascade do |t|
t.string "name", null: false
t.string "uid", null: false
@ -168,20 +158,18 @@ ActiveRecord::Schema.define(version: 20160820231717) do
t.string "scopes", default: "", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.index ["uid"], name: "index_oauth_applications_on_uid", unique: true, using: :btree
end
add_index "oauth_applications", ["uid"], name: "index_oauth_applications_on_uid", unique: true, using: :btree
create_table "stars", force: :cascade do |t|
t.integer "user_id"
t.integer "map_id"
t.datetime "created_at"
t.datetime "updated_at"
t.index ["map_id"], name: "index_stars_on_map_id", using: :btree
t.index ["user_id"], name: "index_stars_on_user_id", using: :btree
end
add_index "stars", ["map_id"], name: "index_stars_on_map_id", using: :btree
add_index "stars", ["user_id"], name: "index_stars_on_user_id", using: :btree
create_table "synapses", force: :cascade do |t|
t.text "desc"
t.text "category"
@ -193,24 +181,22 @@ ActiveRecord::Schema.define(version: 20160820231717) do
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "defer_to_map_id"
t.index ["node1_id", "node1_id"], name: "index_synapses_on_node1_id_and_node1_id", using: :btree
t.index ["node1_id"], name: "index_synapses_on_node1_id", using: :btree
t.index ["node2_id", "node2_id"], name: "index_synapses_on_node2_id_and_node2_id", using: :btree
t.index ["node2_id"], name: "index_synapses_on_node2_id", using: :btree
t.index ["user_id"], name: "index_synapses_on_user_id", using: :btree
end
add_index "synapses", ["node1_id", "node1_id"], name: "index_synapses_on_node1_id_and_node1_id", using: :btree
add_index "synapses", ["node1_id"], name: "index_synapses_on_node1_id", using: :btree
add_index "synapses", ["node2_id", "node2_id"], name: "index_synapses_on_node2_id_and_node2_id", using: :btree
add_index "synapses", ["node2_id"], name: "index_synapses_on_node2_id", using: :btree
add_index "synapses", ["user_id"], name: "index_synapses_on_user_id", using: :btree
create_table "tokens", force: :cascade do |t|
t.string "token"
t.string "description"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["user_id"], name: "index_tokens_on_user_id", using: :btree
end
add_index "tokens", ["user_id"], name: "index_tokens_on_user_id", using: :btree
create_table "topics", force: :cascade do |t|
t.text "name"
t.text "desc"
@ -229,21 +215,19 @@ ActiveRecord::Schema.define(version: 20160820231717) do
t.integer "audio_file_size"
t.datetime "audio_updated_at"
t.integer "defer_to_map_id"
t.index ["metacode_id"], name: "index_topics_on_metacode_id", using: :btree
t.index ["user_id"], name: "index_topics_on_user_id", using: :btree
end
add_index "topics", ["metacode_id"], name: "index_topics_on_metacode_id", using: :btree
add_index "topics", ["user_id"], name: "index_topics_on_user_id", using: :btree
create_table "user_maps", force: :cascade do |t|
t.integer "user_id"
t.integer "map_id"
t.datetime "created_at"
t.datetime "updated_at"
t.index ["map_id"], name: "index_user_maps_on_map_id", using: :btree
t.index ["user_id"], name: "index_user_maps_on_user_id", using: :btree
end
add_index "user_maps", ["map_id"], name: "index_user_maps_on_map_id", using: :btree
add_index "user_maps", ["user_id"], name: "index_user_maps_on_user_id", using: :btree
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
@ -272,19 +256,17 @@ ActiveRecord::Schema.define(version: 20160820231717) do
t.integer "image_file_size"
t.datetime "image_updated_at"
t.integer "generation"
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
end
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
create_table "webhooks", force: :cascade do |t|
t.integer "hookable_id"
t.string "hookable_type"
t.string "kind", null: false
t.string "uri", null: false
t.text "event_types", default: [], array: true
t.index ["hookable_type", "hookable_id"], name: "index_webhooks_on_hookable_type_and_hookable_id", using: :btree
end
add_index "webhooks", ["hookable_type", "hookable_id"], name: "index_webhooks_on_hookable_type_and_hookable_id", using: :btree
add_foreign_key "tokens", "users"
end

39
doc/api/api.raml Normal file
View file

@ -0,0 +1,39 @@
#%RAML 1.0
---
title: Metamaps
version: v2
baseUri: http://metamaps.cc/api/v2
mediaType: application/json
securitySchemes:
- oauth_2_0:
description: |
OAuth 2.0 implementation
type: OAuth 2.0
settings:
authorizationUri: https://metamaps.cc/api/v2/oauth/authorize
accessTokenUri: https://metamaps.cc/api/v2/oauth/token
authorizationGrants: [ authorization_code, password, client_credentials, implicit, refresh_token ]
traits:
- pageable: !include traits/pageable.raml
- orderable: !include traits/orderable.raml
- searchable: !include traits/searchable.raml
schemas:
- topic: !include schemas/_topic.json
- synapse: !include schemas/_synapse.json
- map: !include schemas/_map.json
- mapping: !include schemas/_mapping.json
- token: !include schemas/_token.json
resourceTypes:
- base: !include resourceTypes/base.raml
- item: !include resourceTypes/item.raml
- collection: !include resourceTypes/collection.raml
/topics: !include apis/topics.raml
/synapses: !include apis/synapses.raml
/maps: !include apis/maps.raml
/mappings: !include apis/mappings.raml
/tokens: !include apis/tokens.raml

View file

@ -0,0 +1,68 @@
type: collection
get:
responses:
200:
body:
application/json:
example: !include ../examples/mappings.json
post:
body:
application/json:
properties:
mappable_id:
description: id of the topic/synapse to be mapped
mappable_type:
description: Topic or Synapse
map_id:
description: id of the map
xloc:
description: (for Topic mappings only) x location on the canvas
yloc:
description: (for Topic mappings only) y location on the canvas
responses:
201:
body:
application/json:
example: !include ../examples/mapping.json
/{id}:
type: item
get:
responses:
200:
body:
application/json:
example: !include ../examples/mapping.json
put:
body:
application/json:
properties:
mappable_id:
description: id of the topic/synapse to be mapped
mappable_type:
description: Topic or Synapse
map_id:
description: id of the map
responses:
200:
body:
application/json:
example: !include ../examples/mapping.json
patch:
body:
application/json:
properties:
mappable_id:
description: id of the topic/synapse to be mapped
mappable_type:
description: Topic or Synapse
map_id:
description: id of the map
responses:
200:
body:
application/json:
example: !include ../examples/mapping.json
delete:
responses:
204:
description: No content

82
doc/api/apis/maps.raml Normal file
View file

@ -0,0 +1,82 @@
type: collection
get:
responses:
200:
body:
application/json:
example: !include ../examples/maps.json
post:
body:
application/json:
properties:
name:
description: name
desc:
description: description
permission:
description: commons, public, or private
screenshot:
description: url to a screenshot of the map
contributor_ids:
description: the topic being linked from
collaborator_ids:
description: the topic being linked to
responses:
201:
body:
application/json:
example: !include ../examples/map.json
/{id}:
type: item
get:
responses:
200:
body:
application/json:
example: !include ../examples/map.json
put:
body:
application/json:
properties:
name:
description: name
desc:
description: description
permission:
description: commons, public, or private
screenshot:
description: url to a screenshot of the map
contributor_ids:
description: the topic being linked from
collaborator_ids:
description: the topic being linked to
responses:
200:
body:
application/json:
example: !include ../examples/map.json
patch:
body:
application/json:
properties:
name:
description: name
desc:
description: description
permission:
description: commons, public, or private
screenshot:
description: url to a screenshot of the map
contributor_ids:
description: the topic being linked from
collaborator_ids:
description: the topic being linked to
responses:
200:
body:
application/json:
example: !include ../examples/map.json
delete:
responses:
204:
description: No content

View file

@ -0,0 +1,82 @@
type: collection
get:
responses:
200:
body:
application/json:
example: !include ../examples/synapses.json
post:
body:
application/json:
properties:
desc:
description: name
category:
description: from to or both
permission:
description: commons, public, or private
topic1_id:
description: the topic being linked from
topic2_id:
description: the topic being linked to
user_id:
description: the creator of the topic
responses:
201:
body:
application/json:
example: !include ../examples/synapse.json
/{id}:
type: item
get:
responses:
200:
body:
application/json:
example: !include ../examples/synapse.json
put:
body:
application/json:
properties:
desc:
description: name
category:
description: from-to or both
permission:
description: commons, public, or private
topic1_id:
description: the topic being linked from
topic2_id:
description: the topic being linked to
user_id:
description: the creator of the topic
responses:
200:
body:
application/json:
example: !include ../examples/synapse.json
patch:
body:
application/json:
properties:
desc:
description: name
category:
description: from-to or both
permission:
description: commons, public, or private
topic1_id:
description: the topic being linked from
topic2_id:
description: the topic being linked to
user_id:
description: the creator of the topic
responses:
200:
body:
application/json:
example: !include ../examples/synapse.json
delete:
responses:
204:
description: No content

25
doc/api/apis/tokens.raml Normal file
View file

@ -0,0 +1,25 @@
type: collection
post:
body:
application/json:
properties:
description:
description: short string describing this token
responses:
201:
body:
application/json:
example: !include ../examples/token.json
/my_tokens:
get:
responses:
200:
body:
application/json:
example: !include ../examples/tokens.json
/{id}:
type: item
delete:
responses:
204:
description: No content

72
doc/api/apis/topics.raml Normal file
View file

@ -0,0 +1,72 @@
type: collection
get:
responses:
200:
body:
application/json:
example: !include ../examples/topics.json
post:
body:
application/json:
properties:
name:
description: name
desc:
description: description
link:
description: (optional) link to content on the web
permission:
description: commons, public, or private
metacode_id:
description: Topic's metacode
responses:
201:
body:
application/json:
example: !include ../examples/topic.json
/{id}:
type: item
get:
responses:
200:
body:
application/json:
example: !include ../examples/topic.json
put:
body:
application/json:
properties:
name:
description: name
desc:
description: description
link:
description: (optional) link to content on the web
permission:
description: commons, public, or private
responses:
200:
body:
application/json:
example: !include ../examples/topic.json
patch:
body:
application/json:
properties:
name:
description: name
desc:
description: description
link:
description: (optional) link to content on the web
permission:
description: commons, public, or private
responses:
200:
body:
application/json:
example: !include ../examples/topic.json
delete:
responses:
204:
description: No content

27
doc/api/examples/map.json Normal file
View file

@ -0,0 +1,27 @@
{
"data": {
"id": 2,
"name": "Emergent Network Phenomena",
"desc": "Example map for the API",
"permission": "commons",
"screenshot": "https://s3.amazonaws.com/metamaps-assets/site/missing-map.png",
"created_at": "2016-03-26T08:02:05.379Z",
"updated_at": "2016-03-27T07:20:18.047Z",
"topic_ids": [
58,
59
],
"synapse_ids": [
2
],
"mapping_ids": [
94,
95,
96
],
"collaborator_ids": [],
"contributor_ids": [
2
]
}
}

View file

@ -0,0 +1,11 @@
{
"data": {
"id": 4,
"created_at": "2016-03-25T08:44:21.337Z",
"updated_at": "2016-03-25T08:44:21.337Z",
"mappable_id": 1,
"mappable_type": "Synapse",
"user_id": 1,
"map_id": 1
}
}

View file

@ -0,0 +1,54 @@
{
"data": [
{
"created_at": "2016-03-25T08:44:07.152Z",
"id": 1,
"map_id": 1,
"mappable_id": 1,
"mappable_type": "Topic",
"updated_at": "2016-03-25T08:44:07.152Z",
"user_id": 1,
"xloc": -271,
"yloc": 22
},
{
"created_at": "2016-03-25T08:44:13.907Z",
"id": 2,
"map_id": 1,
"mappable_id": 2,
"mappable_type": "Topic",
"updated_at": "2016-03-25T08:44:13.907Z",
"user_id": 1,
"xloc": -12,
"yloc": 61
},
{
"created_at": "2016-03-25T08:44:19.333Z",
"id": 3,
"map_id": 1,
"mappable_id": 3,
"mappable_type": "Topic",
"updated_at": "2016-03-25T08:44:19.333Z",
"user_id": 1,
"xloc": -93,
"yloc": -90
},
{
"created_at": "2016-03-25T08:44:21.337Z",
"id": 4,
"map_id": 1,
"mappable_id": 1,
"mappable_type": "Synapse",
"updated_at": "2016-03-25T08:44:21.337Z",
"user_id": 1
}
],
"page": {
"current_page": 1,
"next_page": 2,
"per": 4,
"prev_page": 0,
"total_count": 303,
"total_pages": 76
}
}

View file

@ -0,0 +1,37 @@
{
"data": [
{
"id": 2,
"name": "Emergent Network Phenomena",
"desc": "Example map for the API",
"permission": "commons",
"screenshot": "https://s3.amazonaws.com/metamaps-assets/site/missing-map.png",
"created_at": "2016-03-26T08:02:05.379Z",
"updated_at": "2016-03-27T07:20:18.047Z",
"topic_ids": [
58,
59
],
"synapse_ids": [
2
],
"mapping_ids": [
94,
95,
96
],
"collaborator_ids": [],
"contributor_ids": [
2
]
}
],
"page": {
"current_page": 1,
"next_page": 2,
"prev_page": 0,
"total_pages": 5,
"total_count": 5,
"per": 1
}
}

View file

@ -0,0 +1,13 @@
{
"data": {
"id": 2,
"desc": "hello",
"category": "from-to",
"permission": "commons",
"created_at": "2016-03-26T08:02:17.994Z",
"updated_at": "2016-03-26T08:02:17.994Z",
"topic1_id": 5,
"topic2_id": 6,
"user_id": 2
}
}

View file

@ -0,0 +1,34 @@
{
"data": [
{
"id": 2,
"desc": "hello",
"category": "from-to",
"permission": "commons",
"created_at": "2016-03-26T08:02:17.994Z",
"updated_at": "2016-03-26T08:02:17.994Z",
"topic1_id": 1,
"topic2_id": 2,
"user_id": 2
},
{
"id": 6,
"desc": "nice",
"category": "both",
"permission": "public",
"created_at": "2016-03-26T08:05:31.563Z",
"updated_at": "2016-03-26T08:05:31.563Z",
"topic1_id": 2,
"topic2_id": 3,
"user_id": 2
}
],
"page": {
"current_page": 1,
"next_page": 2,
"prev_page": 0,
"total_pages": 71,
"total_count": 142,
"per": 2
}
}

View file

@ -0,0 +1,8 @@
{
"data": {
"id": 1,
"token": "VeI0qAe2bf2ytnrTRxmywsH0VSwuyjK5",
"description": "Personal token for in-browser testing",
"created_at": "2016-09-06T03:47:56.553Z"
}
}

View file

@ -0,0 +1,18 @@
{
"data": [
{
"id": 1,
"token": "VeI0qAe2bf2ytnrTRxmywsH0VSwuyjK5",
"description": "Personal token for in-browser testing",
"created_at": "2016-09-06T03:47:56.553Z"
}
],
"page": {
"current_page": 1,
"next_page": 0,
"prev_page": 0,
"total_pages": 1,
"total_count": 1,
"per": 25
}
}

Some files were not shown because too many files have changed in this diff Show more