Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions lib/travis/api/v3/models/log_parts.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
require 'forwardable'

module Travis::API::V3::Models
class LogParts
extend Forwardable

def_delegators :remote_log

attr_accessor :remote_log, :job, :content

def initialize(remote_log: nil, job: nil, content: true)
@remote_log = remote_log
@job = job
@content = content
end

def log_parts
content ? remote_log : remote_log.map { |rl| rl.as_info_json.compact }
end

def repository_private?
job.repository.private?
end

def repository
@repository ||= Travis::API::V3::Models::Repository.find(job.repository.id)
end
end
end
8 changes: 6 additions & 2 deletions lib/travis/api/v3/queries/log.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ def find_by_job_id(job_id)

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

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

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

private

def remote_log_svc
@remote_log_svc ||= Travis::RemoteLog::Remote.new(platform: platform)
end

def prefix
"jobs/#{platform_job_id}/log.txt"
end
Expand Down
41 changes: 41 additions & 0 deletions lib/travis/api/v3/queries/log_parts.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
module Travis::API::V3
class Queries::LogParts < RemoteQuery
def parts(job, params)
@job = job
content = (params['content'] || 'true') == 'true'
remote_log = remote_log_svc.find_parts_by_job_id(
platform_job_id,
after: params['after'],
part_numbers: params['part_numbers'],
require_all: (params['require_all'] || 'true') == 'true',
content: content
)
raise EntityMissing, 'log not found'.freeze if remote_log.nil?

Travis::API::V3::Models::LogParts.new(remote_log: remote_log, job: job, content: content)
end

private

def remote_log_svc
@remote_log_svc ||= Travis::RemoteLog::Remote.new(platform: platform)
end

def platform
return :default if deployed_on_org?
return :fallback if @job.migrated? && [email protected]_post_migration?

:default
end

def platform_job_id
return @job.org_id if @job.migrated? && [email protected]_post_migration?

@job.id
end

def deployed_on_org?
ENV["TRAVIS_SITE"] == "org"
end
end
end
9 changes: 9 additions & 0 deletions lib/travis/api/v3/renderer/log_parts.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module Travis::API::V3
class Renderer::LogParts < ModelRenderer
representation(:standard, :log_parts)

def log_parts
@model.log_parts
end
end
end
4 changes: 4 additions & 0 deletions lib/travis/api/v3/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ module Routes
delete :delete
end

resource :log_parts do
route '/log_parts'
get :find
end
end

resource :lint do
Expand Down
1 change: 1 addition & 0 deletions lib/travis/api/v3/services.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ module Services
KeyPair = Module.new { extend Services }
Lint = Module.new { extend Services }
Log = Module.new { extend Services }
LogParts = Module.new { extend Services }
ScanResult = Module.new { extend Services }
ScanResults = Module.new { extend Services }
Storage = Module.new { extend Services }
Expand Down
30 changes: 30 additions & 0 deletions lib/travis/api/v3/services/log_parts/find.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
module Travis::API::V3
class Services::LogParts::Find < Service
params :after, :part_numbers, :content, :require_all

def run!
job = Models::Job.find(params['job.id'])
log = query.parts(job, params)
repo_can_write = false
if access_control.is_a?(Travis::API::V3::AccessControl::LogToken)
repo_can_write = access_control.repo_can_write
elsif access_control.user
repo_can_write = !!job.repository.users.where(id: access_control.user.id, permissions: { push: true }).first
raise LogAccessDenied if !Travis.config.legacy_roles &&
!access_control.permissions(job).view_log? && job.repository.private?
end

raise LogExpired if expired?(job)
raise LogAccessDenied if job.repository.user_settings.job_log_access_based_limit &&
(!repo_can_write || !access_control.permissions(job).view_log?)

result log
end

def expired?(job)
!job.repository.user_settings.job_log_time_based_limit &&
job.started_at &&
job.started_at < Time.now - job.repository.user_settings.job_log_access_older_than_days.days
end
end
end
15 changes: 11 additions & 4 deletions lib/travis/remote_log.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,11 @@ def removed?
!removed_by_id.nil?
end

def parts(after: nil, part_numbers: [])
def parts(after: nil, part_numbers: [], require_all: false, content: true)
return solo_part if removed? || aggregated?

remote.find_parts_by_job_id(
job_id, after: after, part_numbers: part_numbers
job_id, after: after, part_numbers: part_numbers, require_all: require_all, content: content
)
end

Expand Down Expand Up @@ -167,11 +168,13 @@ def find_id_by_job_id(job_id)
JSON.parse(resp.body).fetch('id')
end

def find_parts_by_job_id(job_id, after: nil, part_numbers: [])
def find_parts_by_job_id(job_id, after: nil, part_numbers: [], require_all: false, content: true)
resp = conn.get do |req|
req.url "log-parts/#{job_id}"
req.params['after'] = after unless after.nil?
unless part_numbers.empty?
req.params['require_all'] = require_all if require_all
req.params['content'] = content unless content
if part_numbers && !part_numbers.empty?
req.params['part_numbers'] = part_numbers.map(&:to_s).join(',')
end
end
Expand Down Expand Up @@ -352,5 +355,9 @@ class RemoteLogPart
def as_json(**_)
attributes.slice(*%i(content final number))
end

def as_info_json(**_)
attributes.slice(*%i(final number))
end
end
end
2 changes: 1 addition & 1 deletion spec/lib/travis/remote_log_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def initialize(data)
remote = double()
expect(described_class::Remote).to receive(:new).and_return(remote)
allow(remote).to receive(:find_parts_by_job_id)
.with(5, after: nil, part_numbers: [])
.with(5, after: nil, part_numbers: [], content: true, require_all: false)
.and_return(found_parts)
expect(subject.parts).to eq found_parts
end
Expand Down
77 changes: 77 additions & 0 deletions spec/v3/services/log_parts/find_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
require 'spec_helper'

describe Travis::API::V3::Services::LogParts, set_app: true do
let(:user) { FactoryBot.create(:user) }
let(:repo) { FactoryBot.create(:repository, owner_name: user.login, name: 'minimal', owner: user)}
let(:build) { FactoryBot.create(:build, repository: repo) }
let(:perm) { Travis::API::V3::Models::Permission.create(repository: repo, user: repo.owner, pull: true, push: true)}
let(:job) { Travis::API::V3::Models::Job.create(build: build, started_at: Time.now - 10.days, repository: repo) }
let(:token) { Travis::Api::App::AccessToken.create(user: user, app_id: 1) }
let(:headers) { { 'HTTP_AUTHORIZATION' => "token #{token}" } }
let(:parsed_body) { JSON.load(body) }
let(:time) { Time.now }
let :log_parts do
[
{
'content' => 'a\nb\nc\n',
'final' => false,
'number' => 0,
},
{
'content' => 'x',
'final' => true,
'number' => 1,
}
]
end

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'] } }
before { stub_request(:get, %r((.+)/permissions/repo/(.+))).to_return(status: 200, body: JSON.generate(authorization)) }

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'] } }
before { stub_request(:get, %r((.+)/permissions/repo/(.+))).to_return(status: 200, body: JSON.generate(authorization)) }

before do
allow_any_instance_of(Travis::API::V3::AccessControl::LegacyToken).to receive(:visible?).and_return(true)
remote = double('remote')
allow(Travis::RemoteLog::Remote).to receive(:new).and_return(remote)
allow(remote).to receive(:find_parts_by_job_id).and_return(log_parts.map { |part| Travis::RemoteLogPart.new(part)})
end

describe 'returns log with an array of Log Parts' do
let(:authorization) {
{ 'permissions' => ['repository_log_view'] }
}
let(:headers) {{}}
example do

get("/v3/job/#{job.id}/log_parts", {}, headers)

expect(parsed_body).to match(
'@type' => 'log_parts',
'@representation' => 'standard',
'log_parts' => log_parts
)
end
end
describe 'returns log with an array of Log Parts' do
let :log_parts do
[{'content': nil, 'number': 0},{'final': nil, 'number': 1}]
end

let(:authorization) {
{ 'permissions' => ['repository_log_view'] }
}
let(:headers) {{}}
example 'only info' do

get("/v3/job/#{job.id}/log_parts?content=false", {}, headers)

expect(parsed_body).to match(
'@type' => 'log_parts',
'@representation' => 'standard',
'log_parts' => [{'number' => 0},{'number' => 1}]
)
end
end
end