dune-api 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +35 -0
- data/.gitmodules +3 -0
- data/.rspec +2 -0
- data/.travis.yml +19 -0
- data/CHANGELOG.md +48 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +601 -0
- data/LICENSE.txt +22 -0
- data/README.md +19 -0
- data/README.md.backup +61 -0
- data/Rakefile +2 -0
- data/apiary.apib +1769 -0
- data/app/constraints/dune/api/api_constraint.rb +17 -0
- data/app/controllers/dune/api/base_controller.rb +68 -0
- data/app/controllers/dune/api/v1/channels/members_controller.rb +33 -0
- data/app/controllers/dune/api/v1/channels_controller.rb +73 -0
- data/app/controllers/dune/api/v1/investments_controller.rb +70 -0
- data/app/controllers/dune/api/v1/press_assets_controller.rb +38 -0
- data/app/controllers/dune/api/v1/projects_controller.rb +76 -0
- data/app/controllers/dune/api/v1/rewards_controller.rb +9 -0
- data/app/controllers/dune/api/v1/sessions_controller.rb +27 -0
- data/app/controllers/dune/api/v1/tags_controller.rb +37 -0
- data/app/controllers/dune/api/v1/users_controller.rb +26 -0
- data/app/models/dune/api/access_token.rb +24 -0
- data/app/models/dune/api/investment.rb +14 -0
- data/app/models/dune/api/project.rb +23 -0
- data/app/models/dune/api/user_concern.rb +11 -0
- data/app/serializers/channel_member_serializer.rb +7 -0
- data/app/serializers/channel_serializer.rb +34 -0
- data/app/serializers/dune/api/investment_serializer.rb +44 -0
- data/app/serializers/dune/api/project_serializer.rb +86 -0
- data/app/serializers/press_asset_serializer.rb +11 -0
- data/app/serializers/reward_serializer.rb +13 -0
- data/app/serializers/tag_serializer.rb +10 -0
- data/app/serializers/user_serializer.rb +46 -0
- data/bin/rails +8 -0
- data/config/initializers/mime_types.rb +1 -0
- data/config/routes.rb +42 -0
- data/db/migrate/20140624141405_create_dune_api_access_tokens.rb +11 -0
- data/dune-api.gemspec +28 -0
- data/lib/dune/api.rb +12 -0
- data/lib/dune/api/engine.rb +11 -0
- data/lib/dune/api/paginated_controller.rb +19 -0
- data/lib/dune/api/version.rb +5 -0
- data/spec/constraints/neighborly/api/api_constraint_spec.rb +50 -0
- data/spec/controllers/neighborly/api/v1/channels/members_controller_spec.rb +82 -0
- data/spec/controllers/neighborly/api/v1/channels_controller_spec.rb +188 -0
- data/spec/controllers/neighborly/api/v1/investments_controller_spec.rb +178 -0
- data/spec/controllers/neighborly/api/v1/press_assets_controller_spec.rb +129 -0
- data/spec/controllers/neighborly/api/v1/projects_controller_spec.rb +317 -0
- data/spec/controllers/neighborly/api/v1/rewards_controller_spec.rb +28 -0
- data/spec/controllers/neighborly/api/v1/sessions_controller_spec.rb +67 -0
- data/spec/controllers/neighborly/api/v1/tags_controller_spec.rb +143 -0
- data/spec/controllers/neighborly/api/v1/users_controller_spec.rb +43 -0
- data/spec/factories.rb +78 -0
- data/spec/fixtures/image.png +0 -0
- data/spec/models/neighborly/api/investment_spec.rb +33 -0
- data/spec/models/neighborly/api/user_concern_spec.rb +33 -0
- data/spec/spec_helper.rb +43 -0
- data/spec/support/shared_examples.rb +96 -0
- metadata +219 -0
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Dune::Api::V1::SessionsController do
|
4
|
+
routes { Dune::Api::Engine.routes }
|
5
|
+
let!(:user) { FactoryGirl.create(:user) }
|
6
|
+
let(:parsed_response) { JSON.parse(response.body) }
|
7
|
+
|
8
|
+
describe '#create' do
|
9
|
+
it 'responds with 401 when requested with invalid email/password combination' do
|
10
|
+
post :create, email: user.email, password: 'wrong-password'
|
11
|
+
expect(response.status).to eql(401)
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'responds with 401 when requested with invalid email' do
|
15
|
+
post :create, email: 'wrong-email', password: 'wrong-password'
|
16
|
+
expect(response.status).to eql(401)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'responds with 400 when requested with no email' do
|
20
|
+
post :create, password: 'right-password'
|
21
|
+
expect(response.status).to eql(400)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'responds with 400 when requested with no password' do
|
25
|
+
post :create, email: user.email
|
26
|
+
expect(response.status).to eql(400)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'responds with 201 when requested with valid email and password combination' do
|
30
|
+
post :create, email: user.email, password: 'right-password'
|
31
|
+
expect(response.status).to eql(201)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'returns session data when requested with valid email and password combination' do
|
35
|
+
post :create, email: user.email, password: 'right-password'
|
36
|
+
expect(parsed_response['access_token']).to eql(user.get_access_token)
|
37
|
+
expect(parsed_response['user_id']).to eql(user.id)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '#destroy', authorized: true do
|
42
|
+
let(:do_request) { delete :destroy }
|
43
|
+
|
44
|
+
context 'when access_token is provided' do
|
45
|
+
context 'and is valid' do
|
46
|
+
before do
|
47
|
+
request.env['HTTP_AUTHORIZATION'] = "Token token=#{valid_access_token.code}"
|
48
|
+
end
|
49
|
+
let(:valid_access_token) { FactoryGirl.create(:access_token, user: user) }
|
50
|
+
|
51
|
+
it 'responds with 200' do
|
52
|
+
expect(response.status).to eql(200)
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'expires given access_token' do
|
56
|
+
delete :destroy
|
57
|
+
expect(valid_access_token.reload).to be_expired
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'renders empty json' do
|
61
|
+
delete :destroy
|
62
|
+
expect(parsed_response).to be_empty
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Dune::Api::V1::TagsController do
|
4
|
+
routes { Dune::Api::Engine.routes }
|
5
|
+
let(:parsed_response) { JSON.parse(response.body) }
|
6
|
+
|
7
|
+
describe '#index', authorized: true do
|
8
|
+
let!(:tag) { FactoryGirl.create(:tag) }
|
9
|
+
let(:do_request) { get :index, format: :json }
|
10
|
+
|
11
|
+
it_behaves_like 'paginating results'
|
12
|
+
|
13
|
+
it 'responds with 200' do
|
14
|
+
do_request
|
15
|
+
expect(response.status).to eql(200)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'has a top level element called tags' do
|
19
|
+
do_request
|
20
|
+
expect(parsed_response.fetch('tags')).to be_a(Array)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'responds with data of tags' do
|
24
|
+
do_request
|
25
|
+
expect(
|
26
|
+
parsed_response.fetch('tags').first
|
27
|
+
).to have_key('id')
|
28
|
+
end
|
29
|
+
|
30
|
+
describe 'filter by popular' do
|
31
|
+
let!(:popular) { FactoryGirl.create(:tag_popular) }
|
32
|
+
|
33
|
+
it 'accepts truthy popular parameter' do
|
34
|
+
get :index, format: :json, popular: '1'
|
35
|
+
response_ids = parsed_response.fetch('tags').map { |t| t['id'] }
|
36
|
+
expect(response_ids).to eql([popular.id])
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'skip any filtering for popular when receiving falsy value' do
|
40
|
+
get :index, format: :json, popular: '0'
|
41
|
+
response_ids = parsed_response.fetch('tags').map { |t| t['id'] }
|
42
|
+
expect(response_ids).to eql(Tag.pluck(:id))
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '#create', authorized: true, admin: true do
|
48
|
+
let(:do_request) { post :create, tag: { name: 'foobar', visible: true }, format: :json }
|
49
|
+
|
50
|
+
context 'on success' do
|
51
|
+
it 'returns a created http status' do
|
52
|
+
do_request
|
53
|
+
expect(response.status).to eq(201)
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'returns a json' do
|
57
|
+
do_request
|
58
|
+
|
59
|
+
expect(parsed_response.count).to eq(1)
|
60
|
+
expect(parsed_response['tag']['name']).to eq('foobar')
|
61
|
+
expect(parsed_response['tag']['visible']).to eq(true)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'on failure' do
|
66
|
+
let(:do_request) { post :create, tag: { }, format: :json }
|
67
|
+
|
68
|
+
it 'returns a unprocessable entity http status' do
|
69
|
+
do_request
|
70
|
+
expect(response.status).to eq(422)
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'returns a json with errors' do
|
74
|
+
do_request
|
75
|
+
|
76
|
+
expect(parsed_response.count).to eq(1)
|
77
|
+
expect(parsed_response['errors']['name']).not_to be_empty
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe '#update', authorized: true, admin: true do
|
83
|
+
let!(:tag) { FactoryGirl.create(:tag) }
|
84
|
+
|
85
|
+
let(:do_request) do
|
86
|
+
put :update,
|
87
|
+
id: tag,
|
88
|
+
tag: { name: 'foobar', visible: true }, format: :json
|
89
|
+
end
|
90
|
+
|
91
|
+
context 'on success' do
|
92
|
+
it 'returns a no content http status' do
|
93
|
+
do_request
|
94
|
+
expect(response.status).to eq(204)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context 'on failure' do
|
99
|
+
let(:do_request) { put :update, id: tag, tag: { name: '' }, format: :json }
|
100
|
+
|
101
|
+
it 'returns a unprocessable entity http status' do
|
102
|
+
do_request
|
103
|
+
expect(response.status).to eq(422)
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'returns a json with errors' do
|
107
|
+
do_request
|
108
|
+
|
109
|
+
expect(parsed_response.count).to eq(1)
|
110
|
+
expect(parsed_response['errors']['name']).not_to be_empty
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
describe '#show', authorized: true do
|
116
|
+
let!(:tag) { FactoryGirl.create(:tag) }
|
117
|
+
let(:do_request) { get :show, id: tag, format: :json }
|
118
|
+
|
119
|
+
it 'returns a success http status' do
|
120
|
+
do_request
|
121
|
+
expect(response.status).to eq(200)
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'returns a json' do
|
125
|
+
do_request
|
126
|
+
|
127
|
+
expect(parsed_response.count).to eq(1)
|
128
|
+
expect(parsed_response['tag']['name']).to eq(tag.name)
|
129
|
+
expect(parsed_response['tag']['visible']).to eq(tag.visible)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
describe '#destroy', authorized: true, admin: true do
|
134
|
+
let!(:tag) { FactoryGirl.create(:tag) }
|
135
|
+
let(:do_request) { delete :destroy, id: tag, format: :json }
|
136
|
+
|
137
|
+
it 'returns a success http status' do
|
138
|
+
do_request
|
139
|
+
expect(response.status).to eq(204)
|
140
|
+
expect { tag.reload }.to raise_error
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Dune::Api::V1::UsersController do
|
4
|
+
routes { Dune::Api::Engine.routes }
|
5
|
+
let(:parsed_response) { JSON.parse(response.body) }
|
6
|
+
let(:users_returned) do
|
7
|
+
parsed_response.fetch('users').map { |t| t['id'] }
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#index', authorized: true, admin: true do
|
11
|
+
let(:do_request) { get :index, format: :json }
|
12
|
+
|
13
|
+
it_behaves_like 'paginating results'
|
14
|
+
|
15
|
+
it 'filters by query' do
|
16
|
+
FactoryGirl.create(:user, name: 'Ordinary user')
|
17
|
+
_user = FactoryGirl.create(:user, name: 'Wonderful user')
|
18
|
+
get :index, format: :json, query: 'wonderful'
|
19
|
+
expect(users_returned).to eql([_user.id])
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#show', authorized: true do
|
24
|
+
let(:do_request) { get :show, id: user.id, format: :json }
|
25
|
+
|
26
|
+
it 'responds with 200' do
|
27
|
+
do_request
|
28
|
+
expect(response.status).to eql(200)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'has a top level element called user' do
|
32
|
+
do_request
|
33
|
+
expect(parsed_response.fetch('user')).to be_a(Hash)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'responds with data of the given user' do
|
37
|
+
do_request
|
38
|
+
expect(
|
39
|
+
parsed_response.fetch('user')
|
40
|
+
).to have_key('id')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/spec/factories.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
FactoryGirl.define do
|
2
|
+
factory :access_token, class: 'Dune::Api::AccessToken' do
|
3
|
+
user
|
4
|
+
end
|
5
|
+
|
6
|
+
factory :category do
|
7
|
+
name_pt { "category-#{rand}" }
|
8
|
+
end
|
9
|
+
|
10
|
+
factory :project, class: 'Dune::Api::Project' do
|
11
|
+
about 'a-big-text-about-the-project'
|
12
|
+
goal 10_000
|
13
|
+
headline 'attractive-headline'
|
14
|
+
location 'New York, NY'
|
15
|
+
name 'z-project'
|
16
|
+
state :online
|
17
|
+
user
|
18
|
+
category
|
19
|
+
end
|
20
|
+
|
21
|
+
factory :investment do
|
22
|
+
project { create(:project, state: 'online') }
|
23
|
+
user
|
24
|
+
confirmed_at Time.now
|
25
|
+
value 10.00
|
26
|
+
state 'confirmed'
|
27
|
+
credits false
|
28
|
+
end
|
29
|
+
|
30
|
+
factory :tag do
|
31
|
+
name { "subject-#{rand}" }
|
32
|
+
visible true
|
33
|
+
end
|
34
|
+
|
35
|
+
factory :tag_popular, parent: :tag do
|
36
|
+
after(:create) do |resource, evaluator|
|
37
|
+
projects = create_list(:project, 4, state: :online)
|
38
|
+
projects.map do |project|
|
39
|
+
project.tags << resource
|
40
|
+
project.save
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
factory :user do
|
46
|
+
name 'Joãozinho'
|
47
|
+
password 'right-password'
|
48
|
+
email { "person#{rand}@example.com" }
|
49
|
+
confirmed_at { Time.now }
|
50
|
+
end
|
51
|
+
|
52
|
+
factory :channel do
|
53
|
+
user { create(:user, profile_type: 'channel') }
|
54
|
+
name 'Test'
|
55
|
+
description 'Lorem Ipsum'
|
56
|
+
sequence(:permalink) { |n| "#{n}-test-page" }
|
57
|
+
state 'online'
|
58
|
+
end
|
59
|
+
|
60
|
+
factory :channel_member do
|
61
|
+
user
|
62
|
+
channel
|
63
|
+
end
|
64
|
+
|
65
|
+
factory :press_asset do
|
66
|
+
title 'Lorem'
|
67
|
+
url 'http://lorem.com'
|
68
|
+
image File.open("#{Dune::Api::Engine.root}/spec/fixtures/image.png")
|
69
|
+
end
|
70
|
+
|
71
|
+
factory :reward do
|
72
|
+
project
|
73
|
+
title 'Awesome Foo Bar'
|
74
|
+
minimum_value 10.00
|
75
|
+
description 'Foo bar'
|
76
|
+
days_to_delivery 10
|
77
|
+
end
|
78
|
+
end
|
Binary file
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Dune::Api::Investment do
|
4
|
+
describe '.between_values' do
|
5
|
+
it 'returns the investments with value between 15 and 20' do
|
6
|
+
FactoryGirl.create(:investment, value: 10)
|
7
|
+
FactoryGirl.create(:investment, value: 15)
|
8
|
+
FactoryGirl.create(:investment, value: 20)
|
9
|
+
FactoryGirl.create(:investment, value: 21)
|
10
|
+
|
11
|
+
expect(described_class.between_values(15, 20).size).to eq 2
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'removes comma and transform it to float' do
|
15
|
+
expect(described_class).to receive(:where).with('value between ? and ?', 1000.0, 2000.4)
|
16
|
+
described_class.between_values('1,000', '2,000.40')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '.by_project_id' do
|
21
|
+
let(:first_project) { FactoryGirl.create(:project, state: 'online') }
|
22
|
+
let(:second_project) { FactoryGirl.create(:project, state: 'online') }
|
23
|
+
|
24
|
+
before do
|
25
|
+
FactoryGirl.create(:investment, value: 10, project: first_project)
|
26
|
+
FactoryGirl.create(:investment, value: 10, project: second_project)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'returns the investments from the first project' do
|
30
|
+
expect(described_class.by_project_id(first_project.id).size).to eq 1
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe User do
|
4
|
+
describe 'get_access_token' do
|
5
|
+
subject { FactoryGirl.create(:user) }
|
6
|
+
|
7
|
+
context 'when the user does not have an access token' do
|
8
|
+
it 'creates an access token and returns its code' do
|
9
|
+
expect {
|
10
|
+
subject.get_access_token
|
11
|
+
}.to change(subject.access_tokens.reload, :count).by(1)
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'returns the last' do
|
15
|
+
expect(subject.get_access_token).to eql(subject.access_tokens.last.code)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'when the user already has one access token' do
|
20
|
+
before { subject.access_tokens.create }
|
21
|
+
|
22
|
+
it 'skips creation of a new' do
|
23
|
+
expect {
|
24
|
+
subject.get_access_token
|
25
|
+
}.to_not change(subject.access_tokens.reload, :count)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'returns the last' do
|
29
|
+
expect(subject.get_access_token).to eql(subject.access_tokens.last.code)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# Configure Rails Envinronment
|
2
|
+
ENV["RAILS_ENV"] = "test"
|
3
|
+
|
4
|
+
require File.expand_path("../dummy/config/environment.rb", __FILE__)
|
5
|
+
require 'rspec/rails'
|
6
|
+
require 'factories'
|
7
|
+
|
8
|
+
ENGINE_RAILS_ROOT=File.join(File.dirname(__FILE__), '../')
|
9
|
+
|
10
|
+
# Requires supporting ruby files with custom matchers and macros, etc,
|
11
|
+
# in spec/support/ and its subdirectories.
|
12
|
+
Dir[File.join(ENGINE_RAILS_ROOT, "spec/support/**/*.rb")].each {|f| require f }
|
13
|
+
|
14
|
+
require 'sidekiq/testing'
|
15
|
+
Sidekiq::Testing.fake!
|
16
|
+
|
17
|
+
Geocoder.configure(lookup: :test)
|
18
|
+
Geocoder::Lookup::Test.set_default_stub(
|
19
|
+
[
|
20
|
+
{
|
21
|
+
'latitude' => 40.7143528,
|
22
|
+
'longitude' => -74.0059731,
|
23
|
+
'address' => 'New York, NY, USA',
|
24
|
+
'state' => 'New York',
|
25
|
+
'state_code' => 'NY',
|
26
|
+
'country' => 'United States',
|
27
|
+
'country_code' => 'US'
|
28
|
+
}
|
29
|
+
]
|
30
|
+
)
|
31
|
+
|
32
|
+
RSpec.configure do |config|
|
33
|
+
config.include RSpec::Rails::ControllerExampleGroup,
|
34
|
+
file_path: %r(spec/controllers)
|
35
|
+
config.use_transactional_fixtures = true
|
36
|
+
|
37
|
+
# Stubs required from the main application
|
38
|
+
config.before(:each) do
|
39
|
+
double(::UserObserver)
|
40
|
+
allow_any_instance_of(UserObserver).to receive(:after_create)
|
41
|
+
allow_any_instance_of(UserObserver).to receive(:after_save)
|
42
|
+
end
|
43
|
+
end
|