Skip to content

Commit 17b06e2

Browse files
authored
Executions repo sender (#1158)
Repository and sender in executions endpoint Consumption calculation at backend side
1 parent a3bb95f commit 17b06e2

File tree

10 files changed

+253
-7
lines changed

10 files changed

+253
-7
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module Travis::API::V3
2+
class Renderer::ExecutionPerRepo < ModelRenderer
3+
representation :minimal, :repository_id, :os, :credits_consumed, :minutes_consumed, :repository
4+
representation :standard, *representations[:minimal]
5+
end
6+
end
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module Travis::API::V3
2+
class Renderer::ExecutionPerSender < ModelRenderer
3+
representation :minimal, :credits_consumed, :minutes_consumed, :sender_id, :sender
4+
representation :standard, *representations[:minimal]
5+
end
6+
end
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module Travis::API::V3
2+
class Renderer::ExecutionsPerRepo < CollectionRenderer
3+
type :executionsperrepo
4+
collection_key :executionsperrepo
5+
end
6+
end
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module Travis::API::V3
2+
class Renderer::ExecutionsPerSender < CollectionRenderer
3+
type :executionspersender
4+
collection_key :executionspersender
5+
end
6+
end

lib/travis/api/v3/routes.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,16 @@ module Routes
132132
route '/executions'
133133
get :for_owner
134134
end
135+
136+
resource :executions do
137+
route '/executions_per_repo'
138+
get :for_owner_per_repo
139+
end
140+
141+
resource :executions do
142+
route '/executions_per_sender'
143+
get :for_owner_per_sender
144+
end
135145
end
136146

137147
resource :repositories do

lib/travis/api/v3/services/executions/for_owner.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ def run!
1212
raise NotFound unless owner
1313
raise InsufficientAccess unless access_control.visible?(owner)
1414

15-
result query(:executions).for_owner(owner, access_control.user.id, params['page'], params['per_page'],
16-
params['from'], params['to'])
15+
result query(:executions).for_owner(owner, access_control.user.id, params['page'] || 0,
16+
params['per_page'] || 0, params['from'], params['to'])
1717
end
1818
end
1919
end
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
module Travis::API::V3
2+
class Services::Executions::ForOwnerPerRepo < Service
3+
params :from, :to
4+
result_type :executions_per_repo
5+
6+
def run!
7+
raise MethodNotAllowed if Travis.config.org?
8+
raise LoginRequired unless access_control.logged_in?
9+
10+
owner = query(:owner).find
11+
12+
raise NotFound unless owner
13+
raise InsufficientAccess unless access_control.visible?(owner)
14+
15+
results = query(:executions).for_owner(owner, access_control.user.id, 0, 0,
16+
params['from'], params['to'])
17+
result recuce_by_repo(results)
18+
end
19+
20+
def recuce_by_repo(results) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
21+
reduced = []
22+
results.each do |item|
23+
minutes_consumed = calculate_minutes(item.started_at, item.finished_at)
24+
obj = reduced.find { |one| one[:repository_id] == item.repository_id && one[:os] == item.os }
25+
if obj
26+
obj[:credits_consumed] += item.credits_consumed
27+
obj[:minutes_consumed] += minutes_consumed
28+
else
29+
repo = Travis::API::V3::Models::Repository.find(item.repository_id)
30+
reduced << {
31+
repository_id: item.repository_id,
32+
os: item.os,
33+
credits_consumed: item.credits_consumed,
34+
minutes_consumed: minutes_consumed,
35+
repository: Renderer.render_model(repo, mode: :standard)
36+
}
37+
end
38+
end
39+
reduced
40+
end
41+
42+
def calculate_minutes(start, finish)
43+
return 0 if finish.to_s.empty?
44+
45+
((Time.parse(finish.to_s) - Time.parse(start.to_s)) / 60.to_f).ceil
46+
end
47+
end
48+
end
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
module Travis::API::V3
2+
class Services::Executions::ForOwnerPerSender < Service
3+
params :from, :to
4+
result_type :executions_per_sender
5+
6+
def run!
7+
raise MethodNotAllowed if Travis.config.org?
8+
raise LoginRequired unless access_control.logged_in?
9+
10+
owner = query(:owner).find
11+
12+
raise NotFound unless owner
13+
raise InsufficientAccess unless access_control.visible?(owner)
14+
15+
results = query(:executions).for_owner(owner, access_control.user.id, 0, 0,
16+
params['from'], params['to'])
17+
result recuce_by_sender(results)
18+
end
19+
20+
def recuce_by_sender(results) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
21+
reduced = []
22+
results.each do |item|
23+
minutes_consumed = calculate_minutes(item.started_at, item.finished_at)
24+
obj = reduced.find { |one| one[:sender_id] == item.sender_id }
25+
if obj
26+
obj[:credits_consumed] += item.credits_consumed
27+
obj[:minutes_consumed] += minutes_consumed
28+
else
29+
sender = Travis::API::V3::Models::User.find(item.sender_id)
30+
reduced << {
31+
credits_consumed: item.credits_consumed,
32+
minutes_consumed: minutes_consumed,
33+
sender_id: item.sender_id,
34+
sender: Renderer.render_model(sender, mode: :standard)
35+
}
36+
end
37+
end
38+
reduced
39+
end
40+
41+
def calculate_minutes(start, finish)
42+
return 0 if finish.to_s.empty?
43+
44+
((Time.parse(finish.to_s) - Time.parse(start.to_s)) / 60.to_f).ceil
45+
end
46+
end
47+
end

spec/support/billing_spec_helper.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -272,14 +272,14 @@ def billing_executions_response_body(attributes = {})
272272
'virtualization_type' => 'vm',
273273
'queue' => 'builds.gce-oss',
274274
'job_id' => 123,
275-
'repository_id' => 123,
275+
'repository_id' => 1,
276276
'owner_id' => 1,
277277
'owner_type' => 'User',
278278
'plan_id' => 2,
279279
'sender_id' => 1,
280280
'credits_consumed' => 5,
281281
'started_at' => Time.now,
282-
'finished_at' => Time.now,
282+
'finished_at' => Time.now + 10.minutes,
283283
'created_at' => Time.now,
284284
'updated_at' => Time.now
285285
}.deep_merge(attributes)

spec/v3/services/v2_subscription/executions_spec.rb

Lines changed: 120 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
let(:parsed_body) { JSON.load(last_response.body) }
33
let(:billing_url) { 'http://billingfake.travis-ci.com' }
44
let(:billing_auth_key) { 'secret' }
5+
let(:repo) { Travis::API::V3::Models::Repository.where(owner_name: 'svenfuchs', name: 'minimal').first }
56

67
before do
78
Travis.config.host = 'travis-ci.com'
@@ -35,13 +36,15 @@
3536
stub_request(:get, "#{billing_url}/usage/users/#{user.id}/executions?page=#{page}&per_page=#{per_page}&from=#{from.to_s}&to=#{to.to_s}")
3637
.with(basic_auth: ['_', billing_auth_key], headers: { 'X-Travis-User-Id' => user.id })
3738
.to_return(body: JSON.dump([billing_executions_response_body]))
39+
stub_request(:get, "#{billing_url}/usage/users/#{user.id}/executions?page=0&per_page=0&from=#{from.to_s}&to=#{to.to_s}")
40+
.with(basic_auth: ['_', billing_auth_key], headers: { 'X-Travis-User-Id' => user.id })
41+
.to_return(body: JSON.dump([billing_executions_response_body]))
3842
end
3943

4044
it 'responds with list of executions' do
4145
get("/v3/owner/#{user.login}/executions?page=#{page}&per_page=#{per_page}&from=#{from.to_s}&to=#{to.to_s}", {}, headers)
4246

4347
expect(last_response.status).to eq(200)
44-
puts parsed_body
4548
expect(parsed_body).to eql_json({
4649
'@type' => 'executions',
4750
'@href' => "/v3/owner/travis-ci/executions?page=1&per_page=25&from=#{from.to_s}&to=#{to.to_s}",
@@ -56,18 +59,132 @@
5659
'virtualization_type' => 'vm',
5760
'queue' => 'builds.gce-oss',
5861
'job_id' => 123,
59-
'repository_id' => 123,
62+
'repository_id' => repo.id,
6063
'owner_id' => 1,
6164
'owner_type' => 'User',
6265
'plan_id' => 2,
6366
'sender_id' => 1,
6467
'credits_consumed' => 5,
6568
'started_at' => Time.now.to_s,
66-
'finished_at' => Time.now.to_s,
69+
'finished_at' => (Time.now + 10.minutes).to_s,
6770
'created_at' => Time.now.to_s,
6871
'updated_at' => Time.now.to_s
6972
}]
7073
})
7174
end
75+
76+
it 'responds with list of executions per repo' do
77+
get("/v3/owner/#{user.login}/executions_per_repo?from=#{from.to_s}&to=#{to.to_s}", {}, headers)
78+
79+
expect(last_response.status).to eq(200)
80+
expect(parsed_body).to eql_json({
81+
'@type' => 'executionsperrepo',
82+
'@href' => "/v3/owner/travis-ci/executions_per_repo?from=#{from.to_s}&to=#{to.to_s}",
83+
'@representation' => 'standard',
84+
'executionsperrepo' =>
85+
[
86+
{
87+
"repository_id"=>1,
88+
"os"=>"linux",
89+
"credits_consumed"=>5,
90+
"minutes_consumed"=>10,
91+
"repository"=>
92+
{
93+
"@type"=>"repository",
94+
"@href"=>"/repo/1",
95+
"@representation"=>"standard",
96+
"@permissions"=>
97+
{
98+
"read"=>true,
99+
"activate"=>false,
100+
"deactivate"=>false,
101+
"migrate"=>false,
102+
"star"=>false,
103+
"unstar"=>false,
104+
"create_cron"=>false,
105+
"create_env_var"=>false,
106+
"create_key_pair"=>false,
107+
"delete_key_pair"=>false,
108+
"create_request"=>false,
109+
"admin"=>false
110+
},
111+
"id"=>1,
112+
"name"=>"minimal",
113+
"slug"=>"svenfuchs/minimal",
114+
"description"=>nil,
115+
"github_id"=>1,
116+
"vcs_id"=>nil,
117+
"vcs_type"=>"GithubRepository",
118+
"github_language"=>nil,
119+
"active"=>true,
120+
"private"=>false,
121+
"owner"=>{"@type"=>"user", "id"=>1, "login"=>"svenfuchs", "@href"=>"/user/1"},
122+
"owner_name"=>"svenfuchs",
123+
"vcs_name"=>"minimal",
124+
"default_branch"=>{"@type"=>"branch", "@href"=>"/repo/1/branch/master", "@representation"=>"minimal", "name"=>"master"},
125+
"starred"=>false,
126+
"managed_by_installation"=>false,
127+
"active_on_org"=>nil,
128+
"migration_status"=>nil,
129+
"history_migration_status"=>nil,
130+
"shared"=>false,
131+
"config_validation"=>false
132+
}
133+
}
134+
]
135+
})
136+
end
137+
138+
it 'responds with list of executions per sender' do
139+
get("/v3/owner/#{user.login}/executions_per_sender?from=#{from.to_s}&to=#{to.to_s}", {}, headers)
140+
141+
expect(last_response.status).to eq(200)
142+
expect(parsed_body).to eql_json({
143+
'@type' => 'executionspersender',
144+
'@href' => "/v3/owner/travis-ci/executions_per_sender?from=#{from.to_s}&to=#{to.to_s}",
145+
'@representation' => 'standard',
146+
'executionspersender' =>
147+
[
148+
{
149+
"credits_consumed"=>5,
150+
"minutes_consumed"=>10,
151+
"sender_id"=>1,
152+
"sender"=>
153+
{
154+
"@type"=>"user",
155+
"@href"=>"/user/1",
156+
"@representation"=>"standard",
157+
"@permissions"=>{"read"=>true, "sync"=>false},
158+
"id"=>1,
159+
"login"=>"svenfuchs",
160+
"name"=>"Sven Fuchs",
161+
"github_id"=>nil,
162+
"vcs_id"=>nil,
163+
"vcs_type"=>"GithubUser",
164+
"avatar_url"=>"https://0.gravatar.com/avatar/07fb84848e68b96b69022d333ca8a3e2",
165+
"education"=>nil,
166+
"allow_migration"=>false,
167+
"allowance"=>
168+
{
169+
"@type"=>"allowance",
170+
"@representation"=>"minimal",
171+
"subscription_type"=>1,
172+
"public_repos"=>true,
173+
"private_repos"=>false,
174+
"concurrency_limit"=>1,
175+
"user_usage"=>false,
176+
"pending_user_licenses"=>false,
177+
"id"=>1
178+
},
179+
"email"=>nil,
180+
"is_syncing"=>nil,
181+
"synced_at"=>nil,
182+
"recently_signed_up"=>false,
183+
"secure_user_hash"=>nil
184+
}
185+
}
186+
]}
187+
)
188+
end
72189
end
73190
end

0 commit comments

Comments
 (0)