Skip to content

Commit c4a4014

Browse files
jobs api extended - added log_parts query (#1388)
* /job/:id/log_parts endpoint added * skip content param if not needed * fix --------- Co-authored-by: mshahzaib-travis <[email protected]>
1 parent 21c3d1a commit c4a4014

File tree

10 files changed

+209
-7
lines changed

10 files changed

+209
-7
lines changed

lib/travis/api/v3/models/log_parts.rb

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
require 'forwardable'
2+
3+
module Travis::API::V3::Models
4+
class LogParts
5+
extend Forwardable
6+
7+
def_delegators :remote_log
8+
9+
attr_accessor :remote_log, :job, :content
10+
11+
def initialize(remote_log: nil, job: nil, content: true)
12+
@remote_log = remote_log
13+
@job = job
14+
@content = content
15+
end
16+
17+
def log_parts
18+
content ? remote_log : remote_log.map { |rl| rl.as_info_json.compact }
19+
end
20+
21+
def repository_private?
22+
job.repository.private?
23+
end
24+
25+
def repository
26+
@repository ||= Travis::API::V3::Models::Repository.find(job.repository.id)
27+
end
28+
end
29+
end

lib/travis/api/v3/queries/log.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ def find_by_job_id(job_id)
66

77
def find(job)
88
@job = job
9-
remote_log = Travis::RemoteLog::Remote.new(platform: platform).find_by_job_id(platform_job_id)
9+
remote_log = remote_log_svc.find_by_job_id(platform_job_id)
1010
raise EntityMissing, 'log not found'.freeze if remote_log.nil?
1111

1212
log = Travis::API::V3::Models::Log.new(remote_log: remote_log, job: job)
@@ -21,7 +21,7 @@ def find(job)
2121

2222
def delete(user, job)
2323
@job = job
24-
remote_log = Travis::RemoteLog::Remote.new(platform: platform).find_by_job_id(platform_job_id)
24+
remote_log = remote_log_svc.find_by_job_id(platform_job_id)
2525
raise EntityMissing, 'log not found'.freeze if remote_log.nil?
2626
raise LogAlreadyRemoved if remote_log.removed_at || remote_log.removed_by
2727
raise JobUnfinished unless @job.finished_at?
@@ -37,6 +37,10 @@ def delete(user, job)
3737

3838
private
3939

40+
def remote_log_svc
41+
@remote_log_svc ||= Travis::RemoteLog::Remote.new(platform: platform)
42+
end
43+
4044
def prefix
4145
"jobs/#{platform_job_id}/log.txt"
4246
end
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
module Travis::API::V3
2+
class Queries::LogParts < RemoteQuery
3+
def parts(job, params)
4+
@job = job
5+
content = (params['content'] || 'true') == 'true'
6+
remote_log = remote_log_svc.find_parts_by_job_id(
7+
platform_job_id,
8+
after: params['after'],
9+
part_numbers: params['part_numbers'],
10+
require_all: (params['require_all'] || 'true') == 'true',
11+
content: content
12+
)
13+
raise EntityMissing, 'log not found'.freeze if remote_log.nil?
14+
15+
Travis::API::V3::Models::LogParts.new(remote_log: remote_log, job: job, content: content)
16+
end
17+
18+
private
19+
20+
def remote_log_svc
21+
@remote_log_svc ||= Travis::RemoteLog::Remote.new(platform: platform)
22+
end
23+
24+
def platform
25+
return :default if deployed_on_org?
26+
return :fallback if @job.migrated? && !@job.restarted_post_migration?
27+
28+
:default
29+
end
30+
31+
def platform_job_id
32+
return @job.org_id if @job.migrated? && !@job.restarted_post_migration?
33+
34+
@job.id
35+
end
36+
37+
def deployed_on_org?
38+
ENV["TRAVIS_SITE"] == "org"
39+
end
40+
end
41+
end
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module Travis::API::V3
2+
class Renderer::LogParts < ModelRenderer
3+
representation(:standard, :log_parts)
4+
5+
def log_parts
6+
@model.log_parts
7+
end
8+
end
9+
end

lib/travis/api/v3/routes.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ module Routes
9191
delete :delete
9292
end
9393

94+
resource :log_parts do
95+
route '/log_parts'
96+
get :find
97+
end
9498
end
9599

96100
resource :lint do

lib/travis/api/v3/services.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ module Services
4040
KeyPair = Module.new { extend Services }
4141
Lint = Module.new { extend Services }
4242
Log = Module.new { extend Services }
43+
LogParts = Module.new { extend Services }
4344
ScanResult = Module.new { extend Services }
4445
ScanResults = Module.new { extend Services }
4546
Storage = Module.new { extend Services }
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
module Travis::API::V3
2+
class Services::LogParts::Find < Service
3+
params :after, :part_numbers, :content, :require_all
4+
5+
def run!
6+
job = Models::Job.find(params['job.id'])
7+
log = query.parts(job, params)
8+
repo_can_write = false
9+
if access_control.is_a?(Travis::API::V3::AccessControl::LogToken)
10+
repo_can_write = access_control.repo_can_write
11+
elsif access_control.user
12+
repo_can_write = !!job.repository.users.where(id: access_control.user.id, permissions: { push: true }).first
13+
raise LogAccessDenied if !Travis.config.legacy_roles &&
14+
!access_control.permissions(job).view_log? && job.repository.private?
15+
end
16+
17+
raise LogExpired if expired?(job)
18+
raise LogAccessDenied if job.repository.user_settings.job_log_access_based_limit &&
19+
(!repo_can_write || !access_control.permissions(job).view_log?)
20+
21+
result log
22+
end
23+
24+
def expired?(job)
25+
!job.repository.user_settings.job_log_time_based_limit &&
26+
job.started_at &&
27+
job.started_at < Time.now - job.repository.user_settings.job_log_access_older_than_days.days
28+
end
29+
end
30+
end

lib/travis/remote_log.rb

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,11 @@ def removed?
4444
!removed_by_id.nil?
4545
end
4646

47-
def parts(after: nil, part_numbers: [])
47+
def parts(after: nil, part_numbers: [], require_all: false, content: true)
4848
return solo_part if removed? || aggregated?
49+
4950
remote.find_parts_by_job_id(
50-
job_id, after: after, part_numbers: part_numbers
51+
job_id, after: after, part_numbers: part_numbers, require_all: require_all, content: content
5152
)
5253
end
5354

@@ -167,11 +168,13 @@ def find_id_by_job_id(job_id)
167168
JSON.parse(resp.body).fetch('id')
168169
end
169170

170-
def find_parts_by_job_id(job_id, after: nil, part_numbers: [])
171+
def find_parts_by_job_id(job_id, after: nil, part_numbers: [], require_all: false, content: true)
171172
resp = conn.get do |req|
172173
req.url "log-parts/#{job_id}"
173174
req.params['after'] = after unless after.nil?
174-
unless part_numbers.empty?
175+
req.params['require_all'] = require_all if require_all
176+
req.params['content'] = content unless content
177+
if part_numbers && !part_numbers.empty?
175178
req.params['part_numbers'] = part_numbers.map(&:to_s).join(',')
176179
end
177180
end
@@ -352,5 +355,9 @@ class RemoteLogPart
352355
def as_json(**_)
353356
attributes.slice(*%i(content final number))
354357
end
358+
359+
def as_info_json(**_)
360+
attributes.slice(*%i(final number))
361+
end
355362
end
356363
end

spec/lib/travis/remote_log_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ def initialize(data)
108108
remote = double()
109109
expect(described_class::Remote).to receive(:new).and_return(remote)
110110
allow(remote).to receive(:find_parts_by_job_id)
111-
.with(5, after: nil, part_numbers: [])
111+
.with(5, after: nil, part_numbers: [], content: true, require_all: false)
112112
.and_return(found_parts)
113113
expect(subject.parts).to eq found_parts
114114
end
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
require 'spec_helper'
2+
3+
describe Travis::API::V3::Services::LogParts, set_app: true do
4+
let(:user) { FactoryBot.create(:user) }
5+
let(:repo) { FactoryBot.create(:repository, owner_name: user.login, name: 'minimal', owner: user)}
6+
let(:build) { FactoryBot.create(:build, repository: repo) }
7+
let(:perm) { Travis::API::V3::Models::Permission.create(repository: repo, user: repo.owner, pull: true, push: true)}
8+
let(:job) { Travis::API::V3::Models::Job.create(build: build, started_at: Time.now - 10.days, repository: repo) }
9+
let(:token) { Travis::Api::App::AccessToken.create(user: user, app_id: 1) }
10+
let(:headers) { { 'HTTP_AUTHORIZATION' => "token #{token}" } }
11+
let(:parsed_body) { JSON.load(body) }
12+
let(:time) { Time.now }
13+
let :log_parts do
14+
[
15+
{
16+
'content' => 'a\nb\nc\n',
17+
'final' => false,
18+
'number' => 0,
19+
},
20+
{
21+
'content' => 'x',
22+
'final' => true,
23+
'number' => 1,
24+
}
25+
]
26+
end
27+
28+
let(:authorization) { { 'permissions' => ['repository_state_update', 'repository_build_create', 'repository_settings_create', 'repository_settings_update', 'repository_cache_view', 'repository_cache_delete', 'repository_settings_delete', 'repository_log_view', 'repository_log_delete', 'repository_build_cancel', 'repository_build_debug', 'repository_build_restart', 'repository_settings_read', 'repository_scans_view'] } }
29+
before { stub_request(:get, %r((.+)/permissions/repo/(.+))).to_return(status: 200, body: JSON.generate(authorization)) }
30+
31+
let(:authorization) { { 'permissions' => ['repository_state_update', 'repository_build_create', 'repository_settings_create', 'repository_settings_update', 'repository_cache_view', 'repository_cache_delete', 'repository_settings_delete', 'repository_log_view', 'repository_log_delete', 'repository_build_cancel', 'repository_build_debug', 'repository_build_restart', 'repository_settings_read', 'repository_scans_view'] } }
32+
before { stub_request(:get, %r((.+)/permissions/repo/(.+))).to_return(status: 200, body: JSON.generate(authorization)) }
33+
34+
before do
35+
allow_any_instance_of(Travis::API::V3::AccessControl::LegacyToken).to receive(:visible?).and_return(true)
36+
remote = double('remote')
37+
allow(Travis::RemoteLog::Remote).to receive(:new).and_return(remote)
38+
allow(remote).to receive(:find_parts_by_job_id).and_return(log_parts.map { |part| Travis::RemoteLogPart.new(part)})
39+
end
40+
41+
describe 'returns log with an array of Log Parts' do
42+
let(:authorization) {
43+
{ 'permissions' => ['repository_log_view'] }
44+
}
45+
let(:headers) {{}}
46+
example do
47+
48+
get("/v3/job/#{job.id}/log_parts", {}, headers)
49+
50+
expect(parsed_body).to match(
51+
'@type' => 'log_parts',
52+
'@representation' => 'standard',
53+
'log_parts' => log_parts
54+
)
55+
end
56+
end
57+
describe 'returns log with an array of Log Parts' do
58+
let :log_parts do
59+
[{'content': nil, 'number': 0},{'final': nil, 'number': 1}]
60+
end
61+
62+
let(:authorization) {
63+
{ 'permissions' => ['repository_log_view'] }
64+
}
65+
let(:headers) {{}}
66+
example 'only info' do
67+
68+
get("/v3/job/#{job.id}/log_parts?content=false", {}, headers)
69+
70+
expect(parsed_body).to match(
71+
'@type' => 'log_parts',
72+
'@representation' => 'standard',
73+
'log_parts' => [{'number' => 0},{'number' => 1}]
74+
)
75+
end
76+
end
77+
end

0 commit comments

Comments
 (0)