Finish ChunkBuffer implementation and integrate it with Artifact
This commit is contained in:
parent
b4022f5f67
commit
b289d3966b
|
@ -9,76 +9,50 @@ require 'travis/chunk_buffer'
|
|||
init: ->
|
||||
@_super.apply this, arguments
|
||||
|
||||
@addObserver 'job.id', @fetchBody
|
||||
@fetchBody()
|
||||
@addObserver 'job.id', @fetch
|
||||
@fetch()
|
||||
|
||||
@set 'queue', Ember.A([])
|
||||
@set 'parts', Ember.ArrayProxy.create(content: [])
|
||||
@set 'parts', Travis.ChunkBuffer.create(content: [])
|
||||
|
||||
@addObserver 'body', @fetchWorker
|
||||
@fetchWorker()
|
||||
#@addObserver 'body', @fetchWorker
|
||||
#@fetchWorker()
|
||||
|
||||
id: (->
|
||||
@get('job.id')
|
||||
).property('job.id')
|
||||
|
||||
willDestroy: ->
|
||||
@get('parts').destroy()
|
||||
|
||||
clear: ->
|
||||
@set('body', '')
|
||||
@incrementProperty('version')
|
||||
@get('parts').destroy()
|
||||
@set 'parts', Travis.ChunkBuffer.create(content: [])
|
||||
|
||||
fetchBody: ->
|
||||
fetch: ->
|
||||
if jobId = @get('job.id')
|
||||
@removeObserver 'job.id', @fetchBody
|
||||
@removeObserver 'job.id', @fetch
|
||||
|
||||
self = this
|
||||
Travis.ajax.ajax "/jobs/#{jobId}/log.txt?cors_hax=true", 'GET',
|
||||
dataType: 'text'
|
||||
contentType: 'text/plain'
|
||||
success: (data, textStatus, xhr) ->
|
||||
if xhr.status == 204
|
||||
logUrl = xhr.getResponseHeader('X-Log-Location')
|
||||
handlers =
|
||||
json: (json) => @loadParts(json['log']['parts'])
|
||||
text: (text) => @loadText(text)
|
||||
|
||||
# For some reason not all browsers can fetch this header
|
||||
unless logUrl
|
||||
logUrl = self.s3Url("/jobs/#{jobId}/log.txt")
|
||||
Travis.Artifact.Request.create(id: id, handlers: handlers).run() if id = @get('job.id')
|
||||
|
||||
$.ajax
|
||||
url: logUrl
|
||||
type: 'GET'
|
||||
success: (data) ->
|
||||
self.fetchedBody(data)
|
||||
else
|
||||
self.fetchedBody(data)
|
||||
append: (part) ->
|
||||
@get('parts').pushObject(part)
|
||||
|
||||
s3Url: (path) ->
|
||||
endpoint = Travis.config.api_endpoint
|
||||
staging = if endpoint.match(/-staging/) then '-staging' else ''
|
||||
host = Travis.config.api_endpoint.replace(/^https?:\/\//, '').split('.').slice(-2).join('.')
|
||||
"https://s3.amazonaws.com/archive#{staging}.#{host}#{path}"
|
||||
loadParts: (parts) ->
|
||||
console.log 'artifact model: load parts'
|
||||
@append(part) for part in parts
|
||||
@set('isLoaded', true)
|
||||
|
||||
|
||||
fetchedBody: (body) ->
|
||||
@set 'body', body
|
||||
@set 'isLoaded', true
|
||||
|
||||
append: (body) ->
|
||||
if @get('isInitialized')
|
||||
@get('parts').pushObject body
|
||||
@set('body', @get('body') + body)
|
||||
else
|
||||
@get('queue').pushObject(body)
|
||||
|
||||
recordDidLoad: (->
|
||||
if @get('isLoaded')
|
||||
if (body = @get 'body') && @get('parts.length') == 0
|
||||
@get('parts').pushObject body
|
||||
|
||||
@set 'isInitialized', true
|
||||
|
||||
queue = @get('queue')
|
||||
if queue.get('length') > 0
|
||||
@append queue.toArray().join('')
|
||||
).observes('isLoaded')
|
||||
loadText: (text) ->
|
||||
console.log 'artifact model: load text'
|
||||
number = -1
|
||||
@append(number: 1, content: text)
|
||||
@set('isLoaded', true)
|
||||
|
||||
fetchWorker: ->
|
||||
if !@get('workerName') && (body = @get('body'))
|
||||
|
@ -88,3 +62,38 @@ require 'travis/chunk_buffer'
|
|||
worker = worker.trim().split(':')[0]
|
||||
@set('workerName', worker)
|
||||
@removeObserver 'body', @fetchWorker
|
||||
|
||||
Travis.Artifact.Request = Em.Object.extend
|
||||
HEADERS:
|
||||
accept: 'application/vnd.travis-ci.2+json; chunked=true; version=2, text/plain; version=2'
|
||||
|
||||
run: ->
|
||||
Travis.ajax.ajax "/jobs/#{@id}/log?cors_hax=true", 'GET',
|
||||
dataType: 'text'
|
||||
headers: @HEADERS
|
||||
success: (body, status, xhr) => @handle(body, status, xhr)
|
||||
|
||||
handle: (body, status, xhr) ->
|
||||
if xhr.status == 204
|
||||
$.ajax(url: @redirectTo(xhr), type: 'GET', success: @handlers.text)
|
||||
else if @isJson(xhr, body)
|
||||
@handlers.json(JSON.parse(body))
|
||||
else
|
||||
@handlers.text(body)
|
||||
|
||||
redirectTo: (xhr) ->
|
||||
# Firefox can't see the Location header on the xhr response due to the wrong
|
||||
# status code 204. Should be some redirect code but that doesn't work with CORS.
|
||||
xhr.getResponseHeader('Location') || @s3Url()
|
||||
|
||||
s3Url: ->
|
||||
endpoint = Travis.config.api_endpoint
|
||||
staging = if endpoint.match(/-staging/) then '-staging' else ''
|
||||
host = endpoint.replace(/^https?:\/\//, '').split('.').slice(-2).join('.')
|
||||
"https://s3.amazonaws.com/archive#{staging}.#{host}#{path}/jobs/#{@id}/log.txt"
|
||||
|
||||
isJson: (xhr, body) ->
|
||||
# Firefox can't see the Content-Type header on the xhr response due to the wrong
|
||||
# status code 204. Should be some redirect code but that doesn't work with CORS.
|
||||
type = xhr.getResponseHeader('Content-Type') || ''
|
||||
type.indexOf('json') > -1 || body.slice(0, 8) == '{"log":{'
|
||||
|
|
|
@ -97,7 +97,8 @@ Travis.Store = DS.Store.extend
|
|||
|
||||
if event == 'job:log'
|
||||
if job = @find(Travis.Job, data['job']['id'])
|
||||
job.appendLog(data['job']['_log'])
|
||||
console.log 'job:log', data
|
||||
job.appendLog(number: data['job']['number'], content: data['job']['_log'])
|
||||
else if data[type.singularName()]
|
||||
@_loadOne(this, type, data)
|
||||
else if data[type.pluralName()]
|
||||
|
|
|
@ -104,7 +104,7 @@
|
|||
job = @get('job')
|
||||
job.subscribe() if job && !job.get('isFinished')
|
||||
null
|
||||
).property('job', 'job.state')
|
||||
).property('job', 'job.isFinished')
|
||||
|
||||
logUrl: (->
|
||||
repo = @get('job.repo')
|
||||
|
@ -133,7 +133,7 @@
|
|||
@_super.apply this, arguments
|
||||
|
||||
Ember.run.next this, ->
|
||||
if @get 'log.isInitialized'
|
||||
if @get 'log'
|
||||
@logDidChange()
|
||||
|
||||
willDestroy: ->
|
||||
|
@ -148,9 +148,9 @@
|
|||
).observes('log.version')
|
||||
|
||||
logDidChange: (->
|
||||
if @get('log.isInitialized') && @state == 'inDOM'
|
||||
if @get('log') && @state == 'inDOM'
|
||||
@attachLogObservers()
|
||||
).observes('log', 'log.isInitialized')
|
||||
).observes('log')
|
||||
|
||||
attachLogObservers: ->
|
||||
return if @get('logPartsObserversAttached') == Ember.guidFor(@get('log'))
|
||||
|
|
|
@ -1,13 +1,20 @@
|
|||
Travis.ChunkBuffer = Em.ArrayProxy.extend Ember.MutableEnumerable,
|
||||
timeout: 15000
|
||||
start: 0
|
||||
next: 0
|
||||
get = Ember.get
|
||||
|
||||
Travis.ChunkBuffer = Em.ArrayProxy.extend
|
||||
timeout: 5000
|
||||
checkTimeoutFrequency: 1000
|
||||
start: 1
|
||||
next: 1
|
||||
|
||||
init: ->
|
||||
@_super.apply this, arguments
|
||||
|
||||
@lastInsert = 0
|
||||
|
||||
@set('next', @get('start'))
|
||||
|
||||
@checkTimeout()
|
||||
|
||||
if @get('content.length')
|
||||
@get('queue.content').pushObjects @get('content').toArray()
|
||||
|
||||
|
@ -33,13 +40,13 @@ Travis.ChunkBuffer = Em.ArrayProxy.extend Ember.MutableEnumerable,
|
|||
).property()
|
||||
|
||||
contentArrayDidChange: (array, index, removedCount, addedCount) ->
|
||||
console.log 'content array did change'
|
||||
@_super.apply this, arguments
|
||||
|
||||
if addedCount
|
||||
queue = @get('queue.content')
|
||||
queue.pushObjects array.slice(index, index + addedCount)
|
||||
@check()
|
||||
@inserted()
|
||||
|
||||
check: ->
|
||||
queue = @get('queue')
|
||||
|
@ -48,10 +55,35 @@ Travis.ChunkBuffer = Em.ArrayProxy.extend Ember.MutableEnumerable,
|
|||
arrangedContent = @get('arrangedContent')
|
||||
toPush = []
|
||||
|
||||
while queue.get('firstObject.number') == next
|
||||
toPush.pushObject queue.shiftObject().get('content')
|
||||
next += 1
|
||||
while queue.get('firstObject.number') <= next
|
||||
element = queue.shiftObject()
|
||||
if get(element, 'number') == next
|
||||
toPush.pushObject get(element, 'content')
|
||||
next += 1
|
||||
|
||||
arrangedContent.pushObjects toPush if toPush.length
|
||||
if toPush.length
|
||||
arrangedContent.pushObjects toPush
|
||||
|
||||
@set('next', next)
|
||||
|
||||
inserted: ->
|
||||
now = @now()
|
||||
@lastInsert = now
|
||||
|
||||
checkTimeout: ->
|
||||
now = @now()
|
||||
if now - @lastInsert > @get('timeout')
|
||||
@giveUpOnMissingParts()
|
||||
@set 'runLaterId', Ember.run.later(this, @checkTimeout, @get('checkTimeoutFrequency'))
|
||||
|
||||
willDestroy: ->
|
||||
Ember.run.cancel @get('runLaterId')
|
||||
@_super.apply this, arguments
|
||||
|
||||
now: ->
|
||||
(new Date()).getTime()
|
||||
|
||||
giveUpOnMissingParts: ->
|
||||
if number = @get('queue.firstObject.number')
|
||||
@set('next', number)
|
||||
@check()
|
||||
|
|
|
@ -26,11 +26,6 @@ minispade.require 'app'
|
|||
runs ->
|
||||
foo = 'bar'
|
||||
|
||||
_Date = Date
|
||||
@Date = (date) ->
|
||||
new _Date(date || '2012-07-02T00:03:00Z')
|
||||
@Date.UTC = _Date.UTC
|
||||
|
||||
# hacks for missing features in webkit
|
||||
unless Function::bind
|
||||
Function::bind = (oThis) ->
|
||||
|
|
|
@ -5,48 +5,61 @@ describe 'Travis.ChunkBuffer', ->
|
|||
it 'waits for parts to be in order before revealing them', ->
|
||||
buffer = Travis.ChunkBuffer.create(content: [])
|
||||
|
||||
buffer.pushObject createChunk(2, "baz")
|
||||
buffer.pushObject createChunk(1, "bar")
|
||||
buffer.pushObject createChunk(3, "baz")
|
||||
buffer.pushObject createChunk(2, "bar")
|
||||
|
||||
expect(buffer.get('length')).toEqual(0)
|
||||
|
||||
buffer.pushObject createChunk(0, "foo")
|
||||
buffer.pushObject createChunk(1, "foo")
|
||||
|
||||
expect(buffer.get('length')).toEqual(3)
|
||||
|
||||
expect(buffer.toArray()).toEqual(['foo', 'bar', 'baz'])
|
||||
|
||||
it 'ignores a part if it fails to be delivered within timeout', ->
|
||||
expect 4
|
||||
buffer = Travis.ChunkBuffer.create(content: [], timeout: 20, checkTimeoutFrequency: 5)
|
||||
|
||||
buffer = Travis.ChunkBuffer.create(content: [], timeout: 10)
|
||||
|
||||
buffer.pushObject createChunk(2, "baz")
|
||||
buffer.pushObject createChunk(3, "baz")
|
||||
|
||||
expect(buffer.get('length')).toEqual(0)
|
||||
|
||||
buffer.pushObject createChunk(0, "foo")
|
||||
buffer.pushObject createChunk(1, "foo")
|
||||
|
||||
expect(buffer.get('length')).toEqual(1)
|
||||
|
||||
stop()
|
||||
setTimeout( (->
|
||||
waits 40
|
||||
runs ->
|
||||
expect(buffer.get('length')).toEqual(2)
|
||||
expect(buffer.toArray()).toEqual(['foo', 'bar', 'baz'])
|
||||
), 20)
|
||||
expect(buffer.toArray()).toEqual(['foo', 'baz'])
|
||||
|
||||
buffer.destroy()
|
||||
|
||||
it 'works correctly when parts are passed as content', ->
|
||||
content = [createChunk(1, 'bar')]
|
||||
content = [createChunk(2, 'bar')]
|
||||
|
||||
buffer = Travis.ChunkBuffer.create(content: content)
|
||||
|
||||
expect(buffer.get('length')).toEqual(0)
|
||||
|
||||
buffer.pushObject createChunk(0, "foo")
|
||||
buffer.pushObject createChunk(1, "foo")
|
||||
|
||||
expect(buffer.get('length')).toEqual(2)
|
||||
expect(buffer.toArray()).toEqual(['foo', 'bar'])
|
||||
|
||||
it 'works correctly when parts duplicated', ->
|
||||
buffer = Travis.ChunkBuffer.create(content: [])
|
||||
|
||||
buffer.pushObject createChunk(1, "foo")
|
||||
buffer.pushObject createChunk(2, "bar")
|
||||
buffer.pushObject createChunk(3, "baz")
|
||||
|
||||
buffer.pushObject createChunk(2, "bar")
|
||||
buffer.pushObject createChunk(3, "baz")
|
||||
buffer.pushObject createChunk(4, "qux")
|
||||
|
||||
expect(buffer.get('length')).toEqual(4)
|
||||
expect(buffer.toArray()).toEqual(['foo', 'bar', 'baz', 'qux'])
|
||||
|
||||
it 'fires array observers properly', ->
|
||||
changes = []
|
||||
buffer = Travis.ChunkBuffer.create(content: [])
|
||||
|
@ -64,12 +77,12 @@ describe 'Travis.ChunkBuffer', ->
|
|||
changes.pushObject([index, addedCount])
|
||||
).create(content: buffer)
|
||||
|
||||
buffer.pushObject createChunk(1, "baz")
|
||||
buffer.pushObject createChunk(2, "baz")
|
||||
|
||||
expect(buffer.get('length')).toEqual(0)
|
||||
expect(changes.length).toEqual(0)
|
||||
|
||||
buffer.pushObject createChunk(0, "foo")
|
||||
buffer.pushObject createChunk(1, "foo")
|
||||
|
||||
expect(buffer.get('length')).toEqual(2)
|
||||
expect(changes.length).toEqual(1)
|
||||
|
|
Loading…
Reference in New Issue
Block a user