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,129 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Dune::Api::V1::PressAssetsController do
|
4
|
+
routes { Dune::Api::Engine.routes }
|
5
|
+
let(:parsed_response) { JSON.parse(response.body) }
|
6
|
+
|
7
|
+
describe '#index', authorized: true do
|
8
|
+
let!(:press_asset) { FactoryGirl.create(:press_asset) }
|
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 press_assets' do
|
19
|
+
do_request
|
20
|
+
expect(parsed_response.fetch('press_assets')).to be_a(Array)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'responds with data of press_assets' do
|
24
|
+
do_request
|
25
|
+
expect(
|
26
|
+
parsed_response.fetch('press_assets').first
|
27
|
+
).to have_key('id')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#create', authorized: true, admin: true do
|
32
|
+
let(:params) { FactoryGirl.build(:press_asset).attributes.merge(image: Rack::Test::UploadedFile.new("#{Dune::Api::Engine.root}/spec/fixtures/image.png"))}
|
33
|
+
let(:do_request) { post :create, press_asset: params, format: :json }
|
34
|
+
|
35
|
+
context 'on success' do
|
36
|
+
it 'returns a created http status' do
|
37
|
+
do_request
|
38
|
+
expect(response.status).to eq(201)
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'returns a json' do
|
42
|
+
do_request
|
43
|
+
|
44
|
+
expect(parsed_response.count).to eq(1)
|
45
|
+
expect(parsed_response['press_asset']['title']).to eq('Lorem')
|
46
|
+
expect(parsed_response['press_asset']['url']).to eq('http://lorem.com')
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'on failure' do
|
51
|
+
let(:do_request) { post :create, press_asset: { }, format: :json }
|
52
|
+
|
53
|
+
it 'returns a unprocessable entity http status' do
|
54
|
+
do_request
|
55
|
+
expect(response.status).to eq(422)
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'returns a json with errors' do
|
59
|
+
do_request
|
60
|
+
|
61
|
+
expect(parsed_response.count).to eq(1)
|
62
|
+
expect(parsed_response['errors']['title']).not_to be_empty
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe '#update', authorized: true, admin: true do
|
68
|
+
let!(:press_asset) { FactoryGirl.create(:press_asset) }
|
69
|
+
|
70
|
+
let(:do_request) do
|
71
|
+
put :update,
|
72
|
+
id: press_asset,
|
73
|
+
press_asset: { title: 'foobar' }, format: :json
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'on success' do
|
77
|
+
it 'returns a no content http status' do
|
78
|
+
do_request
|
79
|
+
expect(response.status).to eq(204)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'on failure' do
|
84
|
+
let(:do_request) { put :update, id: press_asset, press_asset: { title: '' }, format: :json }
|
85
|
+
|
86
|
+
it 'returns a unprocessable entity http status' do
|
87
|
+
do_request
|
88
|
+
expect(response.status).to eq(422)
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'returns a json with errors' do
|
92
|
+
do_request
|
93
|
+
|
94
|
+
expect(parsed_response.count).to eq(1)
|
95
|
+
expect(parsed_response['errors']['title']).not_to be_empty
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe '#show', authorized: true do
|
101
|
+
let!(:press_asset) { FactoryGirl.create(:press_asset) }
|
102
|
+
let(:do_request) { get :show, id: press_asset, format: :json }
|
103
|
+
|
104
|
+
it 'returns a success http status' do
|
105
|
+
do_request
|
106
|
+
expect(response.status).to eq(200)
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'returns a json' do
|
110
|
+
do_request
|
111
|
+
|
112
|
+
expect(parsed_response.count).to eq(1)
|
113
|
+
expect(parsed_response['press_asset']['title']).to eq(press_asset.title)
|
114
|
+
expect(parsed_response['press_asset']['url']).to eq(press_asset.url)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe '#destroy', authorized: true, admin: true do
|
119
|
+
let!(:press_asset) { FactoryGirl.create(:press_asset) }
|
120
|
+
let(:do_request) { delete :destroy, id: press_asset, format: :json }
|
121
|
+
|
122
|
+
it 'returns a success http status' do
|
123
|
+
do_request
|
124
|
+
expect(response.status).to eq(204)
|
125
|
+
expect { press_asset.reload }.to raise_error
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
@@ -0,0 +1,317 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Dune::Api::V1::ProjectsController do
|
4
|
+
include ActiveSupport::Testing::TimeHelpers
|
5
|
+
routes { Dune::Api::Engine.routes }
|
6
|
+
let(:projects_returned) do
|
7
|
+
parsed_response.fetch('projects').map { |t| t['id'] }
|
8
|
+
end
|
9
|
+
let(:parsed_response) { JSON.parse(response.body) }
|
10
|
+
|
11
|
+
describe '#index', authorized: true do
|
12
|
+
let!(:project) { FactoryGirl.create(:project) }
|
13
|
+
let(:do_request) { get :index, format: :json }
|
14
|
+
|
15
|
+
it_behaves_like 'paginating results'
|
16
|
+
|
17
|
+
describe 'manageable' do
|
18
|
+
before do
|
19
|
+
@draft_project = FactoryGirl.create(:project, state: 'draft', user: user)
|
20
|
+
@online_project = FactoryGirl.create(:project, state: 'online')
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'when filtering by manageable projects' do
|
24
|
+
let(:do_request) { get :index, format: :json, manageable: true }
|
25
|
+
|
26
|
+
context 'when user is not an admin' do
|
27
|
+
let(:user) { FactoryGirl.create(:user, admin: false) }
|
28
|
+
|
29
|
+
it 'returns only mangeable projects' do
|
30
|
+
do_request
|
31
|
+
expect(projects_returned).to include(@draft_project.id)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'when user is an admin' do
|
36
|
+
let(:user) { FactoryGirl.create(:user, admin: true) }
|
37
|
+
|
38
|
+
it 'returns all projects' do
|
39
|
+
do_request
|
40
|
+
expect(projects_returned).to include(@draft_project.id, @online_project.id)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'when not filtering by manageable projects' do
|
46
|
+
let(:do_request) { get :index, format: :json, manageable: false }
|
47
|
+
|
48
|
+
context 'when user is not an admin' do
|
49
|
+
let(:user) { FactoryGirl.create(:user, admin: false) }
|
50
|
+
|
51
|
+
it 'returns only public projects' do
|
52
|
+
do_request
|
53
|
+
expect(projects_returned).to include(@online_project.id)
|
54
|
+
expect(projects_returned).not_to include(@draft_project.id)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'when user is an admin' do
|
59
|
+
let(:user) { FactoryGirl.create(:user, admin: true) }
|
60
|
+
|
61
|
+
it 'returns only public projects' do
|
62
|
+
do_request
|
63
|
+
expect(projects_returned).to include(@online_project.id)
|
64
|
+
expect(projects_returned).not_to include(@draft_project.id)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe 'ordering' do
|
71
|
+
let!(:project_1) { FactoryGirl.create(:project, name: 'abc') }
|
72
|
+
let!(:project_2) { FactoryGirl.create(:project, name: 'xyz') }
|
73
|
+
|
74
|
+
it 'order by given attribute' do
|
75
|
+
get :index, format: :json, order_by: 'name desc'
|
76
|
+
expected_projects = [
|
77
|
+
project.id,
|
78
|
+
project_2.id,
|
79
|
+
project_1.id
|
80
|
+
]
|
81
|
+
expect(projects_returned).to eql(expected_projects)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'filters by query' do
|
86
|
+
FactoryGirl.create(:project, name: 'Ordinary project')
|
87
|
+
project = FactoryGirl.create(:project, name: 'Wonderful project')
|
88
|
+
get :index, format: :json, query: 'wonderful'
|
89
|
+
expect(projects_returned).to eql([project.id])
|
90
|
+
end
|
91
|
+
|
92
|
+
describe 'filter by state' do
|
93
|
+
let!(:draft_project) do
|
94
|
+
FactoryGirl.create(:project, state: :draft, user: user)
|
95
|
+
end
|
96
|
+
|
97
|
+
Project.state_names.each do |state|
|
98
|
+
it "filters by state #{state}" do
|
99
|
+
project = FactoryGirl.create(:project, state: state, user: user)
|
100
|
+
expected_ids = if state.eql?(:draft)
|
101
|
+
[project.id, draft_project.id]
|
102
|
+
else
|
103
|
+
[project.id]
|
104
|
+
end
|
105
|
+
|
106
|
+
get :index, format: :json, state => '1', manageable: true
|
107
|
+
expect(projects_returned).to include(*expected_ids)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
describe 'filtering by created_at' do
|
113
|
+
before do
|
114
|
+
travel_to(10.days.ago) do
|
115
|
+
FactoryGirl.create(:project)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'returns just those projects in the given range' do
|
120
|
+
project = travel_to(3.days.ago) do
|
121
|
+
FactoryGirl.create(:project)
|
122
|
+
end
|
123
|
+
get :index, format: :json,
|
124
|
+
between_created_at: {
|
125
|
+
starts_at: 6.days.ago.to_date.to_s,
|
126
|
+
ends_at: Time.now.to_date.to_s
|
127
|
+
}
|
128
|
+
expect(projects_returned).to eql([project.id])
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
describe 'filtering by expires_at' do
|
133
|
+
before do
|
134
|
+
travel_to(10.days.ago) do
|
135
|
+
FactoryGirl.create(:project, online_date: Date.current, online_days: 1)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'returns just those projects in the given range' do
|
140
|
+
project = travel_to(3.days.ago) do
|
141
|
+
FactoryGirl.create(:project, online_date: Date.current, online_days: 1)
|
142
|
+
end
|
143
|
+
get :index, format: :json,
|
144
|
+
between_expires_at: {
|
145
|
+
starts_at: 6.days.ago.to_date.to_s,
|
146
|
+
ends_at: Time.now.to_date.to_s
|
147
|
+
}
|
148
|
+
expect(projects_returned).to eql([project.id])
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
describe 'filtering by online_date' do
|
153
|
+
before do
|
154
|
+
FactoryGirl.create(:project, online_date: 10.days.from_now)
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'returns just those projects in the given range' do
|
158
|
+
project = FactoryGirl.create(:project, online_date: 3.days.from_now)
|
159
|
+
get :index, format: :json,
|
160
|
+
between_online_date: {
|
161
|
+
starts_at: Time.now.to_date.to_s,
|
162
|
+
ends_at: 6.days.from_now.to_date.to_s
|
163
|
+
}
|
164
|
+
expect(projects_returned).to eql([project.id])
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'checks permissions' do
|
169
|
+
project = FactoryGirl.create(:project, state: :draft)
|
170
|
+
do_request
|
171
|
+
expect(projects_returned).not_to include(project.id)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
describe '#show', authorized: true do
|
176
|
+
let(:project) { FactoryGirl.create(:project, user: user) }
|
177
|
+
let(:do_request) { get :show, id: project.id, format: :json }
|
178
|
+
|
179
|
+
context 'when user has access to the project' do
|
180
|
+
it 'responds with 200' do
|
181
|
+
do_request
|
182
|
+
expect(response.status).to eql(200)
|
183
|
+
end
|
184
|
+
|
185
|
+
it 'has a top level element called project' do
|
186
|
+
do_request
|
187
|
+
expect(parsed_response.fetch('project')).to be_a(Hash)
|
188
|
+
end
|
189
|
+
|
190
|
+
it 'responds with data of the given project' do
|
191
|
+
do_request
|
192
|
+
expect(
|
193
|
+
parsed_response.fetch('project')
|
194
|
+
).to have_key('id')
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
context 'when user does not have access to the project' do
|
199
|
+
let(:project) { FactoryGirl.create(:project, state: 'draft') }
|
200
|
+
|
201
|
+
it 'returns a forbidden http status' do
|
202
|
+
do_request
|
203
|
+
expect(response.status).to eq(403)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
describe '#update', authorized: true do
|
209
|
+
let(:project) { FactoryGirl.create(:project, user: user) }
|
210
|
+
|
211
|
+
let(:do_request) do
|
212
|
+
put :update,
|
213
|
+
id: project.id,
|
214
|
+
project: { name: 'Foo Bar Updated' },
|
215
|
+
format: :json
|
216
|
+
end
|
217
|
+
|
218
|
+
context 'when user has access to the project' do
|
219
|
+
it 'updates the record' do
|
220
|
+
expect(Project).to receive(:update)
|
221
|
+
.with(project.id.to_s, { "name" => 'Foo Bar Updated' })
|
222
|
+
|
223
|
+
do_request
|
224
|
+
end
|
225
|
+
|
226
|
+
context 'on success' do
|
227
|
+
it 'returns a no content http status' do
|
228
|
+
do_request
|
229
|
+
expect(response.status).to eq(204)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
context 'on failure' do
|
234
|
+
let(:do_request) do
|
235
|
+
put :update,
|
236
|
+
id: project.id,
|
237
|
+
project: { name: '' },
|
238
|
+
format: :json
|
239
|
+
end
|
240
|
+
|
241
|
+
it 'returns a unprocessable entity http status' do
|
242
|
+
do_request
|
243
|
+
expect(response.status).to eq(422)
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'returns a json with errors' do
|
247
|
+
do_request
|
248
|
+
|
249
|
+
expect(parsed_response.count).to eq(1)
|
250
|
+
expect(parsed_response['errors']['name']).not_to be_empty
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
context 'when user does not have access to the project' do
|
256
|
+
let(:project) { FactoryGirl.create(:project) }
|
257
|
+
|
258
|
+
it 'does not update the record' do
|
259
|
+
expect(Project).not_to receive(:update)
|
260
|
+
do_request
|
261
|
+
end
|
262
|
+
|
263
|
+
it 'returns a forbidden http status' do
|
264
|
+
do_request
|
265
|
+
expect(response.status).to eq(403)
|
266
|
+
expect(project.reload.deleted?).to be_falsy
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
describe 'destroy', authorized: true do
|
272
|
+
let(:user) { FactoryGirl.create(:user, admin: true) }
|
273
|
+
let(:project) { FactoryGirl.create(:project, user: user, state: :draft) }
|
274
|
+
let(:do_request) { delete :destroy, id: project.id, format: :json }
|
275
|
+
|
276
|
+
context 'when it can be pushed to trash' do
|
277
|
+
it 'returns a success http status' do
|
278
|
+
do_request
|
279
|
+
expect(response.status).to eq(204)
|
280
|
+
expect(project.reload.deleted?).to be_truthy
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
context 'when it cannot be pushed to trash' do
|
285
|
+
let(:project) { FactoryGirl.create(:project, user: user, state: :online) }
|
286
|
+
|
287
|
+
it 'returns a forbidden http status' do
|
288
|
+
do_request
|
289
|
+
expect(response.status).to eq(403)
|
290
|
+
expect(project.reload.deleted?).to be_falsy
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
[:approve, :launch, :reject, :push_to_draft].each do |name|
|
296
|
+
describe "#{name}", authorized: true do
|
297
|
+
let(:user) { FactoryGirl.create(:user, admin: true) }
|
298
|
+
let(:project) { FactoryGirl.create(:project, state: :draft) }
|
299
|
+
let(:do_request) { put name, id: project.id, format: :json }
|
300
|
+
|
301
|
+
it 'returns a success http status' do
|
302
|
+
do_request
|
303
|
+
expect(response.status).to eq(204)
|
304
|
+
end
|
305
|
+
|
306
|
+
it 'authorizes the resource' do
|
307
|
+
expect(controller).to receive(:authorize).with(project)
|
308
|
+
do_request
|
309
|
+
end
|
310
|
+
|
311
|
+
it 'calls the state machine helper to change the state' do
|
312
|
+
expect_any_instance_of(Project).to receive("#{name}!")
|
313
|
+
do_request
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Dune::Api::V1::RewardsController do
|
4
|
+
routes { Dune::Api::Engine.routes }
|
5
|
+
let(:parsed_response) { JSON.parse(response.body) }
|
6
|
+
let!(:reward) { FactoryGirl.create(:reward) }
|
7
|
+
|
8
|
+
describe '#show', authorized: true do
|
9
|
+
let(:do_request) { get :show, id: reward.id, format: :json }
|
10
|
+
|
11
|
+
it 'responds with 200' do
|
12
|
+
do_request
|
13
|
+
expect(response.status).to eql(200)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'has a top level element called reward' do
|
17
|
+
do_request
|
18
|
+
expect(parsed_response.fetch('reward')).to be_a(Hash)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'responds with data of the given reward' do
|
22
|
+
do_request
|
23
|
+
expect(
|
24
|
+
parsed_response.fetch('reward')
|
25
|
+
).to have_key('id')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|