Compare commits

...

38 Commits

Author SHA1 Message Date
Piotr Sarnacki
1cd460e020 Experimental implementation of travis-cmd 2015-11-20 19:09:34 +01:00
Lisa P
59c9fce76c add osx_image to keys-map 2015-11-20 14:47:10 +01:00
Piotr Sarnacki
4a73c17d4c Model#typeKey -> Model#modelName 2015-11-18 17:15:08 +01:00
Piotr Sarnacki
fbe384d5ae Fix key for repo relationship for V3 API 2015-11-18 17:05:32 +01:00
Piotr Sarnacki
e60d16209d Fix loading record by slug
In a repo route we need to find record by slug there is no easy way to
do it with a public finders API, so we need to use adapter and
serializers directly. The problem is that the old way of doing this
didn't use the normalizePayload function and also it didn't add included
records properly. New code properly normalizes response and adds all of
the embedded records that were extracted from the response.
2015-11-18 17:01:59 +01:00
Piotr Sarnacki
bd3b62c264 Fix handling branches
This commit fixes handling of branches when using both V3 and V2. The
changes include:

  * proper definition of relationships that reflect V3 structure, so for
    example build belongs to a branch
  * setting up inverse records for some of the relationships. without
    doing that Ember Data can handle relationships in a surprising way,
    for example if the same record is referenced in 2 places in a
    belongsTo relationship, Ember Data will remove one of the references
    without proper inverse definitions
  * we need to add id when extracting branch as a relationship. Ember
    Data expects all of the relationships to have an id
  * lastly, we need to mimic the structure of the V3 API in V2 payloads,
    so for a build payload I'm now creating a branch record
2015-11-18 17:00:34 +01:00
Piotr Sarnacki
9a9708df31 Fix references in V3 payloads
V3 API doesn't return any of the records more than 2 times. If a record
is already included in the response any other occurences will be
represented as a reference, ie. a hash with just an @href. Ember Data
doesn't play nice with such references as it needs an id to identify a
record.

The code in this commit traverses payloads from V3 API and adds an id to
each of the references that are present.

For example a following payload:

    {
      "@href": "/build/1",
      "@type": "build"
      "id": 1,
      "state": "passed",
      "branch": {
        "@href": "/repo/1/branch/master",
        "name": "master",
        "lastBuild": {
          "@href": "/build/1"
        }
      }
    }

Will be changed to:

    {
      "@href": "/build/1",
      "@type": "build"
      "id": 1,
      "state": "passed",
      "branch": {
        "@href": "/repo/1/branch/master",
        "name": "master",
        "lastBuild": {
          "@href": "/build/1",
          "id": 1
        }
      }
    }

In this case an "id" field was added to "branch.lastBuild" field.
2015-11-18 16:48:21 +01:00
Piotr Sarnacki
042e058f2a Update ember-cli-coffeescript to newest version 2015-11-18 16:47:31 +01:00
Piotr Sarnacki
76c604a39c Update ember-cli to 1.13.12 (ember.js 1.13.10, ember-data 1.13.15) 2015-11-18 12:30:53 +01:00
Piotr Sarnacki
df0f883ad0 app/serializers/repo.coffee -> app/serializers/repo.js 2015-11-18 11:48:59 +01:00
Piotr Sarnacki
4e1976d30c Simplify pusher handling in store 2015-11-17 11:48:29 +01:00
Piotr Sarnacki
9222276f87 Remove console.log 2015-11-12 16:13:45 +01:00
Piotr Sarnacki
2267babac0 Fix pusher to work with new ember-data
This commit just fixes things to the point where pusher updates are
applied to the store properly. This still lacks a business logic fixes,
so for example we won't update lastBuild's field, because there's no
such information from pusher.
2015-11-12 16:02:53 +01:00
Piotr Sarnacki
706ea836e4 Don't run Sauce tests for now 2015-11-12 12:44:04 +01:00
Piotr Sarnacki
b4ad8a54b7 Disable dashboard test for now 2015-11-12 12:25:55 +01:00
Piotr Sarnacki
d1b9bddfe0 Fix sidebar repos list
We need this list to update with pusher, so filtering is better than
using store.query.
2015-11-11 17:38:59 +01:00
Piotr Sarnacki
8956dedeaf Attribute mappings in serializers should use underscore notation 2015-11-11 16:24:23 +01:00
Piotr Sarnacki
c072738d58 Use job_ids as a key for jobs relationship for build 2015-11-11 15:37:55 +01:00
Piotr Sarnacki
0ceb677aed Don't add commit data to included array in build serializer
We already do it in an abstracted way (ie. for all relationships) in v2
fallback serializer.
2015-11-11 15:17:04 +01:00
Piotr Sarnacki
cf1e5d6ad3 No need to add repo to the list of attributes now 2015-11-11 15:15:36 +01:00
Piotr Sarnacki
3f6ad9cf1a Fix handling relationships for V2 API
* we should look for both embedded relationship and relationship key,
  so in cases like for commit, when there's a full commit data on
  "commit" property, and only id at "commit_id", we will use commit data
* we can't add @type to V2 fallback, because in other places we chack
  for @type to distinguish V2 and V3 payloads
* there's no need to include a record in "included" if there's only a
  type and an id there
2015-11-11 15:15:36 +01:00
Piotr Sarnacki
c6bac57047 Fix job route's use of job.build relationship
build is an async relationship now, so job.get('build') returns a
promise.
2015-11-11 14:50:45 +01:00
Piotr Sarnacki
4a62d4ac9c Fix including to-many relationships for V3 payloads 2015-11-11 12:38:44 +01:00
Piotr Sarnacki
5a2c86697b Properly include relationships for V2 payloads 2015-11-11 12:38:22 +01:00
Piotr Sarnacki
f09742abe1 Convert build serializer to javascript 2015-11-11 11:52:29 +01:00
Piotr Sarnacki
b83e70c324 Update serializers to work with jobs endpoint response 2015-11-10 17:54:43 +01:00
Piotr Sarnacki
5c5c433363 Include repositoryId as an attribute on build
For some reason (probably some problem with one of the serializers) we
sometimes lack an id attribute for a promise that we get for a repo
relationship on build. Because of that doing `build.get('repo.id')` may
sometimes return undefined. A temporary workaround is to make sure that
we always can access the `repository_id` property.
2015-11-10 17:52:32 +01:00
Piotr Sarnacki
264d780931 Use V2FallbackSerializer for jobs 2015-11-10 09:58:59 +01:00
Piotr Sarnacki
1efdcf1290 Don't throw error if broadcastArray is undefined 2015-11-10 09:58:37 +01:00
Piotr Sarnacki
3d7833d64b Use storage service instead of Travis.storage 2015-11-09 14:06:42 +01:00
Piotr Sarnacki
18838acd27 Remove unused settings/index controller 2015-11-09 13:48:31 +01:00
Piotr Sarnacki
bc4b666d9e Observe repos.firstObject on reposController
Since we change repos property on reposController, we can't set observer
on repos, because as soon as it's changed, we loose the observer.
Instead, we should observe only on reposController, which is not going
to change.
2015-11-09 13:48:25 +01:00
Piotr Sarnacki
310627cc55 Sort repos in repos-list component, not in controller 2015-11-09 13:46:55 +01:00
Piotr Sarnacki
2a2b3f018b Use lastBuild from defaultBranch on repository
One thing that is not standard here is a serializer for branch, which
uses @href as id. At this point branches don't have ids and ember-data
needs one, so using @href is the easiest way.
2015-11-09 13:46:54 +01:00
Piotr Sarnacki
6992a79a93 Create adapters and serializers working with v3 and v2 APIs
This commit adds adapters and serializers for v3, but also a fallback
serializer for v2, which allows to handle v2 and v3 payloads at the same
time. This is needed, because when we use v3 endpoint for one of the
models (in this case repo), we can also get embedded records of other
types (like branch or build).
2015-11-09 13:46:54 +01:00
Piotr Sarnacki
0976672f9e Remove unneeded test 2015-11-09 13:46:54 +01:00
Piotr Sarnacki
22d4f59ce9 Move ajax and auth into services 2015-11-09 13:46:54 +01:00
Piotr Sarnacki
b9de261388 Remove some more deprecations 2015-11-09 13:43:56 +01:00
115 changed files with 1384 additions and 840 deletions

3
.watchmanconfig Normal file
View File

@ -0,0 +1,3 @@
{
"ignore_dirs": ["tmp", "dist"]
}

View File

@ -2,6 +2,8 @@
`import config from 'travis/config/environment'` `import config from 'travis/config/environment'`
Adapter = DS.ActiveModelAdapter.extend Adapter = DS.ActiveModelAdapter.extend
auth: Ember.inject.service()
host: config.apiEndpoint host: config.apiEndpoint
coalesceFindRequests: true coalesceFindRequests: true
@ -12,12 +14,12 @@ Adapter = DS.ActiveModelAdapter.extend
hash.headers['accept'] = 'application/json; version=2' hash.headers['accept'] = 'application/json; version=2'
if token = Travis.sessionStorage.getItem('travis.token') if token = @get('auth').token()
hash.headers['Authorization'] ||= "token #{token}" hash.headers['Authorization'] ||= "token #{token}"
hash hash
findMany: (store, type, ids) -> findMany: (store, type, ids) ->
@ajax(@buildURL(type.typeKey), 'GET', data: { ids: ids }) @ajax(@buildURL(type.modelName), 'GET', data: { ids: ids })
`export default Adapter` `export default Adapter`

24
app/adapters/repo.js Normal file
View File

@ -0,0 +1,24 @@
import V3Adapter from 'travis/adapters/v3';
export default V3Adapter.extend({
buildUrl(modelName, id, snapshot, requestType, query) {
var url = this._super(...arguments);
return url;
},
ajaxOptions(url, type, options) {
var hash = options || {};
if(!hash.data) {
hash.data = {};
}
if(hash.data.include) {
hash.data.include += ',repository.default_branch,branch.last_build,build.commit';
} else {
hash.data.include = 'repository.default_branch,branch.last_build,build.commit';
}
return this._super(url, type, hash);
}
});

60
app/adapters/v3.js Normal file
View File

@ -0,0 +1,60 @@
import Ember from 'ember';
import DS from 'ember-data';
import config from 'travis/config/environment';
export default DS.RESTAdapter.extend({
auth: Ember.inject.service(),
host: config.apiEndpoint,
defaultSerializer: '-repo',
sortQueryParams: false,
coalesceFindRequests: false,
headers: {
'Travis-API-Version': '3',
'Accept': 'application/json',
'Content-Type': 'application/json'
},
ajaxOptions: function(url, type, options) {
var hash = this._super(...arguments);
hash.headers = hash.headers || {};
var token;
if(token = this.get('auth').token()) {
hash.headers['Authorization'] = "token " + token;
}
return hash;
},
// TODO: I shouldn't override this method as it's private, a better way would
// be to create my own URL generator
_buildURL: function(modelName, id) {
var url = [];
var host = Ember.get(this, 'host');
var prefix = this.urlPrefix();
var path;
if (modelName) {
path = this.pathForType(modelName, id);
if (path) { url.push(path); }
}
if (id) { url.push(encodeURIComponent(id)); }
if (prefix) { url.unshift(prefix); }
url = url.join('/');
if (!host && url && url.charAt(0) !== '/') {
url = '/' + url;
}
return url;
},
pathForType: function(modelName, id) {
var underscored = Ember.String.underscore(modelName);
return id ? underscored : Ember.String.pluralize(underscored);
}
});

View File

@ -1,7 +1,7 @@
`import Ember from 'ember'` `import Ember from 'ember'`
`import Ajax from 'travis/utils/ajax'`
CachesItemComponent = Ember.Component.extend CachesItemComponent = Ember.Component.extend
ajax: Ember.inject.service()
tagName: 'li' tagName: 'li'
classNames: ['tile', 'tile--xs', 'row'] classNames: ['tile', 'tile--xs', 'row']
@ -20,7 +20,7 @@ CachesItemComponent = Ember.Component.extend
deletingDone = => @set('isDeleting', false) deletingDone = => @set('isDeleting', false)
repo = @get('repo') repo = @get('repo')
Ajax.ajax("/repos/#{repo.get('id')}/caches", "DELETE", data: data).then(deletingDone, deletingDone).then => @get('ajax').ajax("/repos/#{repo.get('id')}/caches", "DELETE", data: data).then(deletingDone, deletingDone).then =>
@get('caches').removeObject(@get('cache')) @get('caches').removeObject(@get('cache'))

View File

@ -1,10 +1,8 @@
`import Ember from 'ember'` `import Ember from 'ember'`
`import Ajax from 'travis/utils/ajax'`
`import config from 'travis/config/environment'` `import config from 'travis/config/environment'`
NoBuildsComponent = Ember.Component.extend NoBuildsComponent = Ember.Component.extend
actions:
actions:
triggerBuild: () -> triggerBuild: () ->
@set('isLoading', true) @set('isLoading', true)
apiEndpoint = config.apiEndpoint apiEndpoint = config.apiEndpoint

View File

@ -16,8 +16,8 @@ ReposListItemComponent = Ember.Component.extend Polling,
).property('selectedRepo') ).property('selectedRepo')
color: (-> color: (->
colorForState(@get('repo.lastBuildState')) colorForState(@get('repo.defaultBranch.lastBuild.state'))
).property('repo.lastBuildState') ).property('repo.defaultBranch.lastBuild.state')
scrollTop: (-> scrollTop: (->
if (window.scrollY > 0) if (window.scrollY > 0)

View File

@ -1,6 +0,0 @@
`import Ember from 'ember'`
ReposListComponent = Ember.Component.extend
tagName: 'ul'
`export default ReposListComponent`

View File

@ -0,0 +1,66 @@
import Ember from 'ember';
var sortCallback = function(repo1, repo2) {
// this function could be made simpler, but I think it's clearer this way
// what're we really trying to achieve
var lastBuild1 = repo1.get('defaultBranch.lastBuild');
var lastBuild2 = repo2.get('defaultBranch.lastBuild');
if(!lastBuild1 && !lastBuild2) {
// if both repos lack builds, put newer repo first
return repo1.get('id') > repo2.get('id') ? -1 : 1;
} else if(lastBuild1 && !lastBuild2) {
// if only repo1 has a build, it goes first
return -1;
} else if(lastBuild2 && !lastBuild1) {
// if only repo2 has a build, it goes first
return 1;
}
var finishedAt1 = lastBuild1.get('finishedAt');
var finishedAt2 = lastBuild2.get('finishedAt');
if(finishedAt1) {
finishedAt1 = new Date(finishedAt1);
}
if(finishedAt2) {
finishedAt2 = new Date(finishedAt2);
}
if(finishedAt1 && finishedAt2) {
// if both builds finished, put newer first
return finishedAt1.getTime() > finishedAt2.getTime() ? -1 : 1;
} else if(finishedAt1 && !finishedAt2) {
// if repo1 finished, but repo2 didn't, put repo2 first
return 1;
} else if(finishedAt2 && !finishedAt1) {
// if repo2 finisher, but repo1 didn't, put repo1 first
return -1;
} else {
// none of the builds finished, put newer build first
return lastBuild1.get('id') > lastBuild2.get('id') ? -1 : 1;
}
throw "should not happen";
};
var ReposListComponent = Ember.Component.extend({
tagName: 'ul',
sortedRepos: function() {
var repos = this.get('repos');
if(repos && repos.toArray) {
repos = repos.toArray();
}
if(repos && repos.sort) {
return repos.sort(sortCallback);
} else {
return [];
}
}.property('repos.[]', 'repos')
});
export default ReposListComponent;

View File

@ -0,0 +1,84 @@
import Ember from 'ember';
import config from 'travis/config/environment';
let Component = Ember.Component.extend({
keyboard: Ember.inject.service(),
auth: Ember.inject.service(),
classNameBindings: ['visible'],
classNames: ['travis-cmd'],
didInsertElement() {
this.get('keyboard').bind('t', this.show.bind(this));
this.get('keyboard').bind('esc', 'cmd', this.hide.bind(this));
},
show() {
this.loadSlugs();
this.set('visible', true);
this.get('keyboard').setScope('cmd');
},
hide() {
this.set('visible', false);
this.get('keyboard').setScope(null);
this.set('matches', null);
this.set('filterString', null);
this.set('results', null);
},
loadSlugs() {
this.set('loading', true);
$.ajax(config.apiEndpoint + '/repos/slugs', {
headers: {
Authorization: 'token ' + this.get('auth').token(),
Accept: 'application/json; version=2'
}
}).then((data) => {
this.set('loading', false);
this.set('repos', data.repositories);
this.onLoad();
});
},
onLoad() {
setTimeout( () => {
this.$('.input').focus();
}, 10);
},
actions: {
filterChanged(value) {
let list = this.get('repos');
let options = {
pre: '<strong>'
, post: '</strong>'
, extract: function(el) { return el.slug; }
};
let results = fuzzy.filter(value, list, options);
let matches = results.map(function(el) { return el.string; });
this.set('matches', matches.slice(0, 10).join('<br/>'));
this.set('results', results);
this.set('filterString', value);
},
keypress(event) {
if(event.keyCode === 27) {
this.hide();
} else if(event.keyCode === 13) {
let results;
if(results = this.get('results')) {
if(results[0]) {
let slug = results[0].original.slug;
console.log(slug);
}
}
}
}
}
});
export default Component;

View File

@ -1,5 +1,4 @@
`import Ember from 'ember'` `import Ember from 'ember'`
`import Ajax from 'travis/utils/ajax'`
`import config from 'travis/config/environment'` `import config from 'travis/config/environment'`
TravisStatusComponent = Ember.Component.extend TravisStatusComponent = Ember.Component.extend

View File

@ -2,8 +2,7 @@
Controller = Ember.Controller.extend Controller = Ember.Controller.extend
allHooks: [] allHooks: []
needs: ['currentUser'] userBinding: 'auth.currentUser'
userBinding: 'controllers.currentUser.model'
init: -> init: ->
@_super.apply this, arguments @_super.apply this, arguments
@ -22,7 +21,7 @@ Controller = Ember.Controller.extend
reloadHooks: -> reloadHooks: ->
if login = @get('model.login') if login = @get('model.login')
hooks = @store.find('hook', all: true, owner_name: login) hooks = @store.query('hook', all: true, owner_name: login)
hooks.then () -> hooks.then () ->
hooks.set('isLoaded', true) hooks.set('isLoaded', true)

View File

@ -1,7 +1,7 @@
`import Ember from 'ember'` `import Ember from 'ember'`
Controller = Ember.Controller.extend Controller = Ember.Controller.extend
needs: ['currentUser', 'repos'] repos: Ember.inject.controller()
userBinding: 'controllers.currentUser' userBinding: 'auth.currentUser'
`export default Controller` `export default Controller`

View File

@ -3,11 +3,11 @@
`import GithubUrlPropertievs from 'travis/mixins/github-url-properties'` `import GithubUrlPropertievs from 'travis/mixins/github-url-properties'`
Controller = Ember.Controller.extend GithubUrlPropertievs, Controller = Ember.Controller.extend GithubUrlPropertievs,
needs: ['repo', 'currentUser'] repoController: Ember.inject.controller('repo')
repoBinding: 'controllers.repo.repo' repoBinding: 'repoController.repo'
commitBinding: 'build.commit' commitBinding: 'build.commit'
currentUserBinding: 'controllers.currentUser.model' currentUserBinding: 'auth.currentUser'
tabBinding: 'controllers.repo.tab' tabBinding: 'repoController.tab'
sendFaviconStateChanges: true sendFaviconStateChanges: true
currentItemBinding: 'build' currentItemBinding: 'build'

View File

@ -4,10 +4,10 @@ Controller = Ember.ArrayController.extend
sortAscending: false sortAscending: false
sortProperties: ['number'] sortProperties: ['number']
needs: ['repo'] repoController: Ember.inject.controller('repo')
repoBinding: 'controllers.repo.repo' repoBinding: 'repoController.repo'
tabBinding: 'controllers.repo.tab' tabBinding: 'repoController.tab'
isLoadedBinding: 'content.isLoaded' isLoadedBinding: 'content.isLoaded'
isLoadingBinding: 'content.isLoading' isLoadingBinding: 'content.isLoading'
@ -53,7 +53,7 @@ Controller = Ember.ArrayController.extend
if type? if type?
options.event_type = type.replace(/s$/, '') # poor man's singularize options.event_type = type.replace(/s$/, '') # poor man's singularize
@store.find('build', options) @store.query('build', options)
actions: actions:
showMoreBuilds: -> showMoreBuilds: ->

View File

@ -1,9 +1,10 @@
`import Ember from 'ember'` `import Ember from 'ember'`
`import Ajax from 'travis/utils/ajax'`
Controller = Ember.Controller.extend Controller = Ember.Controller.extend
needs: ['repo'] ajax: Ember.inject.service()
repo: Ember.computed.alias('controllers.repo.repo')
repoController: Ember.inject.controller('repo')
repo: Ember.computed.alias('repoController.repo')
isDeleting: false isDeleting: false
@ -21,7 +22,7 @@ Controller = Ember.Controller.extend
deletingDone = => @set('isDeleting', false) deletingDone = => @set('isDeleting', false)
repo = @get('repo') repo = @get('repo')
Ajax.ajax("/repos/#{@get('repo.id')}/caches", "DELETE").then(deletingDone, deletingDone).then => @get('ajax').ajax("/repos/#{@get('repo.id')}/caches", "DELETE").then(deletingDone, deletingDone).then =>
@set('model', {}) @set('model', {})
`export default Controller` `export default Controller`

View File

@ -1,48 +0,0 @@
`import Ember from 'ember'`
`import Validations from 'travis/utils/validations'`
Controller = Ember.ObjectController.extend Validations,
isEditing: false
isDeleting: false
validates:
name: ['presence']
actionType: 'Save'
showValueField: Ember.computed.alias('public')
value: ( (key, value) ->
if arguments.length == 2
@get('model').set('value', value)
value
else if @get('public')
@get('model.value')
else
'••••••••••••••••'
).property('model.value', 'public')
actions:
delete: ->
return if @get('isDeleting')
@set('isDeleting', true)
@get('model').destroyRecord()
edit: ->
@set('isEditing', true)
cancel: ->
@set('isEditing', false)
@get('model').revert()
save: ->
return if @get('isSaving')
if @isValid()
env_var = @get('model')
# TODO: handle errors
env_var.save().then =>
@set('isEditing', false)
`export default Controller`

View File

@ -1,6 +0,0 @@
`import Ember from 'ember'`
Controller = Ember.ArrayController.extend
vars: Ember.computed.alias('model')
`export default Controller`

View File

@ -1,45 +0,0 @@
`import Validations from 'travis/utils/validations'`
Controller = Ember.Controller.extend Validations,
needs: ['repo']
repo: Ember.computed.alias('controllers.repo.repo')
isSaving: false
validates:
name: ['presence']
actionType: 'Add'
showValueField: true
reset: ->
@setProperties(name: null, value: null, public: null)
actions:
cancel: ->
@reset()
@transitionToRoute('env_vars')
save: ->
return if @get('isSaving')
@set('isSaving', true)
if @isValid()
env_var = @store.createRecord('env_var',
name: @get('name')
value: @get('value')
public: @get('public')
repo: @get('repo')
)
self = this
env_var.save().then =>
@set('isSaving', false)
@reset()
self.transitionToRoute('env_vars')
, =>
@set('isSaving', false)
else
@set('isSaving', false)
`export default Controller`

View File

@ -1,8 +1,7 @@
`import Ember from 'ember'` `import Ember from 'ember'`
Controller = Ember.Controller.extend Controller = Ember.Controller.extend
needs: ['currentUser'] user: Ember.computed.alias('auth.currentUser')
user: Ember.computed.alias('controllers.currentUser.model')
isSyncing: Ember.computed.alias('user.isSyncing') isSyncing: Ember.computed.alias('user.isSyncing')

View File

@ -2,13 +2,13 @@
`import { githubCommit } from 'travis/utils/urls'` `import { githubCommit } from 'travis/utils/urls'`
Controller = Ember.Controller.extend Controller = Ember.Controller.extend
needs: ['repo', 'currentUser'] repoController: Ember.inject.controller('repo')
repoBinding: 'controllers.repo.repo' repoBinding: 'repoController.repo'
commitBinding: 'job.commit' commitBinding: 'job.commit'
annotationsBinding: 'job.annotations' annotationsBinding: 'job.annotations'
currentUserBinding: 'controllers.currentUser.model' currentUserBinding: 'auth.currentUser'
tabBinding: 'controllers.repo.tab' tabBinding: 'repoController.tab'
currentItemBinding: 'job' currentItemBinding: 'job'

View File

@ -1,5 +1,4 @@
`import Ember from 'ember'` `import Ember from 'ember'`
`import Ajax from 'travis/utils/ajax'`
Controller = Ember.Controller.extend Controller = Ember.Controller.extend
isLoading: false isLoading: false

View File

@ -1,5 +1,4 @@
`import Ember from 'ember'` `import Ember from 'ember'`
`import Ajax from 'travis/utils/ajax'`
Controller = Ember.Controller.extend Controller = Ember.Controller.extend
isLoading: false isLoading: false
@ -13,14 +12,13 @@ Controller = Ember.Controller.extend
item item
).sortBy('default_branch.last_build.finished_at').reverse() ).sortBy('default_branch.last_build.finished_at').reverse()
repos repos
).property('model') ).property('model')
# running: (-> # running: (->
# data = @get('model') # data = @get('model')
# repos = data.repositories.filter (item, index) -> # repos = data.repositories.filter (item, index) ->
# if item.default_branch.last_build != null # if item.default_branch.last_build != null
# if item.default_branch.last_build.state == 'started' # if item.default_branch.last_build.state == 'started'
# item # item
# repos # repos

View File

@ -1,5 +1,4 @@
`import Ember from 'ember'` `import Ember from 'ember'`
`import Ajax from 'travis/utils/ajax'`
Controller = Ember.Controller.extend Controller = Ember.Controller.extend
isLoading: false isLoading: false

View File

@ -3,16 +3,17 @@
Controller = Ember.Controller.extend Controller = Ember.Controller.extend
name: 'profile' name: 'profile'
needs: ['currentUser', 'accounts', 'account'] accountController: Ember.inject.controller('account')
userBinding: 'controllers.currentUser.model' accountsController: Ember.inject.controller('accounts')
accountBinding: 'controllers.account.model' userBinding: 'auth.currentUser'
accountBinding: 'accountController.model'
activate: (action, params) -> activate: (action, params) ->
this["view_#{action}".camelize()]() this["view_#{action}".camelize()]()
viewHooks: -> viewHooks: ->
@connectTab('hooks') @connectTab('hooks')
@get('controllers.account').reloadHooks() @get('accountController').reloadHooks()
viewUser: -> viewUser: ->
@connectTab('user') @connectTab('user')

View File

@ -2,11 +2,13 @@
`import { githubRepo } from 'travis/utils/urls'` `import { githubRepo } from 'travis/utils/urls'`
Controller = Ember.Controller.extend Controller = Ember.Controller.extend
needs: ['repos', 'currentUser', 'build', 'job'] jobController: Ember.inject.controller('job')
currentUserBinding: 'controllers.currentUser.model' buildController: Ember.inject.controller('build')
reposController: Ember.inject.controller('repos')
currentUserBinding: 'auth.currentUser'
build: Ember.computed.alias('controllers.build.build') build: Ember.computed.alias('buildController.build')
job: Ember.computed.alias('controllers.job.job') job: Ember.computed.alias('jobController.job')
slug: (-> @get('repo.slug') ).property('repo.slug') slug: (-> @get('repo.slug') ).property('repo.slug')
isLoading: (-> @get('repo.isLoading') ).property('repo.isLoading') isLoading: (-> @get('repo.isLoading') ).property('repo.isLoading')
@ -73,15 +75,15 @@ Controller = Ember.Controller.extend
Ember.run.scheduleOnce('actions', this, @_lastBuildDidChange); Ember.run.scheduleOnce('actions', this, @_lastBuildDidChange);
_lastBuildDidChange: -> _lastBuildDidChange: ->
build = @get('repo.lastBuild') build = @get('repo.defaultBranch.lastBuild')
@set('build', build) @set('build', build)
stopObservingLastBuild: -> stopObservingLastBuild: ->
@removeObserver('repo.lastBuild', this, 'lastBuildDidChange') @removeObserver('repo.defaultBranch.lastBuild', this, 'lastBuildDidChange')
observeLastBuild: -> observeLastBuild: ->
@lastBuildDidChange() @lastBuildDidChange()
@addObserver('repo.lastBuild', this, 'lastBuildDidChange') @addObserver('repo.defaultBranch.lastBuild', this, 'lastBuildDidChange')
connectTab: (tab) -> connectTab: (tab) ->
# TODO: such implementation seems weird now, because we render # TODO: such implementation seems weird now, because we render

View File

@ -3,7 +3,6 @@
`import Repo from 'travis/models/repo'` `import Repo from 'travis/models/repo'`
Controller = Ember.Controller.extend Controller = Ember.Controller.extend
contentBinding: 'repos'
actions: actions:
activate: (name) -> activate: (name) ->
@activate(name) @activate(name)
@ -30,15 +29,15 @@ Controller = Ember.Controller.extend
if @get('tab') == 'owned' && @get('isLoaded') && @get('repos.length') == 0 if @get('tab') == 'owned' && @get('isLoaded') && @get('repos.length') == 0
@container.lookup('router:main').send('redirectToGettingStarted') @container.lookup('router:main').send('redirectToGettingStarted')
isLoadedBinding: 'repos.isLoaded' isLoaded: false
needs: ['currentUser', 'repo'] repoController: Ember.inject.controller('repo')
currentUserBinding: 'controllers.currentUser.model' currentUserBinding: 'auth.currentUser'
selectedRepo: (-> selectedRepo: (->
# we need to observe also repo.content here, because we use # we need to observe also repo.content here, because we use
# ObjectProxy in repo controller # ObjectProxy in repo controller
# TODO: get rid of ObjectProxy there # TODO: get rid of ObjectProxy there
@get('controllers.repo.repo.content') || @get('controllers.repo.repo') @get('repoController.repo.content') || @get('repoController.repo')
).property('controllers.repo.repo', 'controllers.repo.repo.content') ).property('repoController.repo', 'repoController.repo.content')
startedJobsCount: Ember.computed.alias('runningJobs.length') startedJobsCount: Ember.computed.alias('runningJobs.length')
allJobsCount: (-> allJobsCount: (->
@ -87,20 +86,23 @@ Controller = Ember.Controller.extend
this["view_#{tab}".camelize()](params) this["view_#{tab}".camelize()](params)
viewOwned: -> viewOwned: ->
@set('repos', @get('userRepos')) @set('isLoaded', false);
@get('currentUser._rawPermissions').then (data) =>
repos = Repo.accessibleBy(@store, data.pull).then( (reposRecordArray) =>
@set('isLoaded', true)
@set('repos', reposRecordArray)
)
# TODO: handle error
viewRunning: -> viewRunning: ->
userRepos: (->
if login = @get('currentUser.login')
Repo.accessibleBy(@store, login)
else
[]
).property('currentUser.login')
viewSearch: (phrase) -> viewSearch: (phrase) ->
@set('search', phrase) @set('search', phrase)
@set('repos', Repo.search(@store, phrase)) @set('isLoaded', false)
Repo.search(@store, phrase).then( (reposRecordArray) =>
@set('isLoaded', true)
@set('repos', reposRecordArray)
)
searchObserver: (-> searchObserver: (->
search = @get('search') search = @get('search')

View File

@ -1,12 +1,11 @@
`import Ember from 'ember'` `import Ember from 'ember'`
Controller = Ember.ArrayController.extend Controller = Ember.ArrayController.extend
needs: ['repo'] repoController: Ember.inject.controller('repo')
repo: Ember.computed.alias('controllers.repo.repo')
lintUrl: (-> lintUrl: (->
slug = @get('repo.slug') slug = @get('repoController.repo.slug')
"https://lint.travis-ci.org/#{slug}" "https://lint.travis-ci.org/#{slug}"
).property('repo.slug') ).property('repoController.repo.slug')
`export default Controller` `export default Controller`

View File

@ -1,22 +0,0 @@
`import Ember from 'ember'`
Controller = Ember.Controller.extend
settings: Ember.computed.alias('model.settings')
settingsChanged: (->
value = @get('settings.maximum_number_of_builds')
console.log value
if parseInt(value) > 0 || value == '0' || value == 0
@set('settings.maximum_number_of_builds_valid', '')
@get('model').saveSettings(@get('settings')).then null, ->
Travis.flash(error: 'There was an error while saving settings. Please try again.')
else
@set('settings.maximum_number_of_builds_valid', 'invalid')
).observes('settings.maximum_number_of_builds')
actions:
save: ->
@get('model').saveSettings(@get('settings')).then null, ->
Travis.flash(error: 'There was an error while saving settings. Please try again.')
`export default Controller`

View File

@ -3,10 +3,10 @@
`import config from 'travis/config/environment'` `import config from 'travis/config/environment'`
Controller = Ember.Controller.extend Controller = Ember.Controller.extend
needs: ['currentUser'] userBinding: 'auth.currentUser'
userBinding: 'controllers.currentUser.model'
store: Ember.inject.service() store: Ember.inject.service()
storage: Ember.inject.service()
currentUserBinding: 'auth.currentUser' currentUserBinding: 'auth.currentUser'
userName: (-> userName: (->
@ -18,6 +18,8 @@ Controller = Ember.Controller.extend
).property('user.gravatarId') ).property('user.gravatarId')
defineTowerColor: (broadcastArray) -> defineTowerColor: (broadcastArray) ->
return '' unless broadcastArray
if broadcastArray.length if broadcastArray.length
if broadcastArray.findBy('category', 'warning') if broadcastArray.findBy('category', 'warning')
return 'warning' return 'warning'
@ -39,7 +41,7 @@ Controller = Ember.Controller.extend
options.type = 'GET' options.type = 'GET'
options.headers = { Authorization: "token #{@auth.token()}" } options.headers = { Authorization: "token #{@auth.token()}" }
seenBroadcasts = Travis.storage.getItem('travis.seen_broadcasts') seenBroadcasts = @get('storage').getItem('travis.seen_broadcasts')
if seenBroadcasts if seenBroadcasts
seenBroadcasts = JSON.parse(seenBroadcasts) seenBroadcasts = JSON.parse(seenBroadcasts)
else else
@ -73,13 +75,13 @@ Controller = Ember.Controller.extend
markBroadcastAsSeen: (broadcast) -> markBroadcastAsSeen: (broadcast) ->
id = broadcast.get('id').toString() id = broadcast.get('id').toString()
seenBroadcasts = Travis.storage.getItem('travis.seen_broadcasts') seenBroadcasts = @get('storage').getItem('travis.seen_broadcasts')
if seenBroadcasts if seenBroadcasts
seenBroadcasts = JSON.parse(seenBroadcasts) seenBroadcasts = JSON.parse(seenBroadcasts)
else else
seenBroadcasts = [] seenBroadcasts = []
seenBroadcasts.push(id) seenBroadcasts.push(id)
Travis.storage.setItem('travis.seen_broadcasts', JSON.stringify(seenBroadcasts)) @get('storage').setItem('travis.seen_broadcasts', JSON.stringify(seenBroadcasts))
@get('broadcasts.content').removeObject(broadcast) @get('broadcasts.content').removeObject(broadcast)
@set('broadcasts.lastBroadcastStatus', @defineTowerColor(@get('broadcasts.content'))) @set('broadcasts.lastBroadcastStatus', @defineTowerColor(@get('broadcasts.content')))
return false return false

View File

@ -1,6 +1,6 @@
`import { safe, formatCommit as formatCommitHelper } from 'travis/utils/helpers'` `import { safe, formatCommit as formatCommitHelper } from 'travis/utils/helpers'`
helper = Ember.HTMLBars.makeBoundHelper (params) -> helper = Ember.Helper.helper (params) ->
commit = params[0] commit = params[0]
safe formatCommitHelper(commit.get('sha'), commit.get('branch')) if commit safe formatCommitHelper(commit.get('sha'), commit.get('branch')) if commit

View File

@ -1,7 +1,7 @@
`import { timeInWords, safe } from 'travis/utils/helpers'` `import { timeInWords, safe } from 'travis/utils/helpers'`
`import Ember from "ember"` `import Ember from "ember"`
helper = Ember.HTMLBars.makeBoundHelper (params) -> helper = Ember.Helper.helper (params) ->
safe timeInWords(params[0]) safe timeInWords(params[0])
`export default helper` `export default helper`

View File

@ -1,7 +1,7 @@
`import { formatMessage as _formatMessage, safe } from 'travis/utils/helpers'` `import { formatMessage as _formatMessage, safe } from 'travis/utils/helpers'`
`import Ember from "ember"` `import Ember from "ember"`
helper = Ember.HTMLBars.makeBoundHelper (params, hash) -> helper = Ember.Helper.helper (params, hash) ->
safe _formatMessage(params[0], hash) safe _formatMessage(params[0], hash)
`export default helper` `export default helper`

View File

@ -1,7 +1,7 @@
`import { formatSha as _formatSha, safe } from 'travis/utils/helpers'` `import { formatSha as _formatSha, safe } from 'travis/utils/helpers'`
`import Ember from "ember"` `import Ember from "ember"`
helper = Ember.HTMLBars.makeBoundHelper (params) -> helper = Ember.Helper.helper (params) ->
safe _formatSha(params[0]) safe _formatSha(params[0])
`export default helper` `export default helper`

View File

@ -1,7 +1,7 @@
`import { timeAgoInWords, safe } from 'travis/utils/helpers'` `import { timeAgoInWords, safe } from 'travis/utils/helpers'`
`import Ember from "ember"` `import Ember from "ember"`
helper = Ember.HTMLBars.makeBoundHelper (params) -> helper = Ember.Helper.helper (params) ->
safe timeAgoInWords(params[0]) || '-' safe timeAgoInWords(params[0]) || '-'
`export default helper` `export default helper`

View File

@ -1,7 +1,7 @@
`import { formatCommit, safe } from 'travis/utils/helpers'` `import { formatCommit, safe } from 'travis/utils/helpers'`
`import { githubCommit as githubCommitUrl } from 'travis/utils/urls'` `import { githubCommit as githubCommitUrl } from 'travis/utils/urls'`
helper = Ember.HTMLBars.makeBoundHelper (params) -> helper = Ember.Helper.helper (params) ->
slug = params[0] slug = params[0]
commitSha = params[1] commitSha = params[1]
return '' unless commitSha return '' unless commitSha

View File

@ -1,7 +1,7 @@
`import { safe } from 'travis/utils/helpers'` `import { safe } from 'travis/utils/helpers'`
`import Ember from "ember"` `import Ember from "ember"`
helper = Ember.HTMLBars.makeBoundHelper (params) -> helper = Ember.Helper.helper (params) ->
state = params[0] state = params[0]
if state == 'received' if state == 'received'
'booting' 'booting'

View File

@ -1,7 +1,7 @@
`import { timeAgoInWords, safe } from 'travis/utils/helpers'` `import { timeAgoInWords, safe } from 'travis/utils/helpers'`
`import Ember from "ember"` `import Ember from "ember"`
helper = Ember.HTMLBars.makeBoundHelper (params) -> helper = Ember.Helper.helper (params) ->
safe timeAgoInWords(params[0]) || 'currently running' safe timeAgoInWords(params[0]) || 'currently running'
`export default helper` `export default helper`

View File

@ -1,7 +1,7 @@
`import { timeAgoInWords, safe } from 'travis/utils/helpers'` `import { timeAgoInWords, safe } from 'travis/utils/helpers'`
`import Ember from "ember"` `import Ember from "ember"`
helper = Ember.HTMLBars.makeBoundHelper (params) -> helper = Ember.Helper.helper (params) ->
safe moment(params[0]).format('MMMM D, YYYY H:mm:ss') || '-' safe moment(params[0]).format('MMMM D, YYYY H:mm:ss') || '-'
`export default helper` `export default helper`

View File

@ -1,7 +1,7 @@
`import { pathFrom } from 'travis/utils/helpers'` `import { pathFrom } from 'travis/utils/helpers'`
`import Ember from "ember"` `import Ember from "ember"`
helper = Ember.HTMLBars.makeBoundHelper (params) -> helper = Ember.Helper.helper (params) ->
url = params[0] url = params[0]
path = pathFrom(url) path = pathFrom(url)
if path.indexOf('...') >= 0 if path.indexOf('...') >= 0

View File

@ -4,4 +4,4 @@ fn = (size) ->
if size if size
(size / 1024 / 1024).toFixed(2) (size / 1024 / 1024).toFixed(2)
`export default Ember.HTMLBars.makeBoundHelper(fn)` `export default Ember.Helper.helper(fn)`

View File

@ -1,16 +1,11 @@
`import Auth from 'travis/utils/auth'`
`import TestAuth from 'travis/utils/test-auth'` `import TestAuth from 'travis/utils/test-auth'`
initialize = (container, app) -> initialize = (container, app) ->
app.register 'auth:main', if Ember.testing then TestAuth else Auth app.inject('route', 'auth', 'service:auth')
app.inject('controller', 'auth', 'service:auth')
app.inject('route', 'auth', 'auth:main') app.inject('application', 'auth', 'service:auth')
app.inject('controller', 'auth', 'auth:main') app.inject('component', 'auth', 'service:auth')
app.inject('application', 'auth', 'auth:main') app.inject('service:flashes', 'auth', 'service:auth')
app.inject('component', 'auth', 'auth:main')
app.inject('service:flashes', 'auth', 'auth:main')
app.inject('auth', 'store', 'service:store')
AuthInitializer = AuthInitializer =
name: 'auth' name: 'auth'

View File

@ -2,12 +2,7 @@
`import TravisPusher from 'travis/utils/pusher'` `import TravisPusher from 'travis/utils/pusher'`
initialize = (registry, application) -> initialize = (registry, application) ->
if config.pusher.key null
application.pusher = new TravisPusher(config.pusher)
application.register 'pusher:main', application.pusher, { instantiate: false }
application.inject('route', 'pusher', 'pusher:main')
PusherInitializer = PusherInitializer =
name: 'pusher' name: 'pusher'

View File

@ -1,16 +1,11 @@
`import Slider from 'travis/utils/slider'`
`import Tailing from 'travis/utils/tailing'` `import Tailing from 'travis/utils/tailing'`
`import ToTop from 'travis/utils/to-top'` `import ToTop from 'travis/utils/to-top'`
`import config from 'travis/config/environment'` `import config from 'travis/config/environment'`
initialize = (container, application) -> initialize = (container, application) ->
application.slider = new Slider(application.storage)
application.tailing = new Tailing($(window), '#tail', '#log') application.tailing = new Tailing($(window), '#tail', '#log')
application.toTop = new ToTop($(window), '.to-top', '#log-container') application.toTop = new ToTop($(window), '.to-top', '#log-container')
application.register 'slider:main', application.slider, { instantiate: false }
application.inject('controller', 'slider', 'slider:main')
Initializer = Initializer =
name: 'services' name: 'services'
initialize: initialize initialize: initialize

View File

@ -1,58 +0,0 @@
`import Ember from 'ember'`
Storage = Em.Object.extend
init: ->
@set('storage', {})
key: (key) ->
"__#{key.replace('.', '__')}"
getItem: (k) ->
return @get("storage.#{@key(k)}")
setItem: (k,v) ->
@set("storage.#{@key(k)}", v)
removeItem: (k) ->
@setItem(k, null)
clear: ->
@set('storage', {})
sessionStorage = (->
storage = null
try
# firefox will not throw error on access for sessionStorage var,
# you need to actually get something from session
window.sessionStorage.getItem('foo')
storage = window.sessionStorage
catch err
storage = Storage.create()
storage
)()
storage = (->
storage = null
try
storage = window.localStorage || throw('no storage')
catch err
storage = Storage.create()
storage
)()
initialize = (container, application) ->
application.register 'storage:main', storage, { instantiate: false }
application.register 'sessionStorage:main', sessionStorage, { instantiate: false }
application.inject('auth', 'storage', 'storage:main')
application.inject('auth', 'sessionStorage', 'sessionStorage:main')
# I still use Travis.storage in some places which are not that easy to
# refactor
application.storage = storage
application.sessionStorage = sessionStorage
StorageInitializer =
name: 'storage'
before: 'services'
initialize: initialize
`export {initialize}`
`export default StorageInitializer`

View File

@ -1,5 +1,17 @@
`import config from 'travis/config/environment'`
`import TravisPusher from 'travis/utils/pusher'`
initialize = (data) -> initialize = (data) ->
data.application.pusher.store = data.container.lookup('service:store') application = data.application
if config.pusher.key
application.pusher = new TravisPusher(config.pusher)
application.register 'pusher:main', application.pusher, { instantiate: false }
application.inject('route', 'pusher', 'pusher:main')
application.pusher.store = data.container.lookup('service:store')
PusherInitializer = PusherInitializer =
name: 'pusher' name: 'pusher'

View File

@ -2,25 +2,11 @@
`import Model from 'travis/models/model'` `import Model from 'travis/models/model'`
Branch = Model.extend Branch = Model.extend
repositoryId: DS.attr('number') name: DS.attr('string')
commitId: DS.attr('number') defaultBranch: DS.attr('boolean')
state: DS.attr()
number: DS.attr('number')
branch: DS.attr()
message: DS.attr()
result: DS.attr('number')
duration: DS.attr('number')
startedAt: DS.attr()
finishedAt: DS.attr()
commit: DS.belongsTo('commit') lastBuild: DS.belongsTo('build')
builds: DS.hasMany('builds', inverse: 'branch')
repo: (-> repo: DS.belongsTo('repo', inverse: 'defaultBranch')
@store.find('repo', @get('repositoryId')) if @get('repositoryId')
).property('repositoryId')
updateTimes: ->
@notifyPropertyChange 'started_at'
@notifyPropertyChange 'finished_at'
`export default Branch` `export default Branch`

View File

@ -18,7 +18,7 @@ Broadcast = Model.extend
Broadcast.reopenClass Broadcast.reopenClass
seen: (-> seen: (->
seenBroadcasts = Travis.storage.getItem('travis.seen_broadcasts') seenBroadcasts = Travis.lookup('service:storage').getItem('travis.seen_broadcasts')
seenBroadcasts = JSON.parse(seenBroadcasts) if seenBroadcasts? seenBroadcasts = JSON.parse(seenBroadcasts) if seenBroadcasts?
Ember.A(seenBroadcasts || []) Ember.A(seenBroadcasts || [])
).property() ).property()

View File

@ -1,26 +1,28 @@
`import { durationFrom, configKeys, compact } from 'travis/utils/helpers'` `import { durationFrom, configKeys, compact } from 'travis/utils/helpers'`
`import Ajax from 'travis/utils/ajax'`
`import configKeysMap from 'travis/utils/keys-map'` `import configKeysMap from 'travis/utils/keys-map'`
`import Ember from 'ember'` `import Ember from 'ember'`
`import Model from 'travis/models/model'` `import Model from 'travis/models/model'`
`import DurationCalculations from 'travis/utils/duration-calculations'` `import DurationCalculations from 'travis/utils/duration-calculations'`
Build = Model.extend DurationCalculations, Build = Model.extend DurationCalculations,
ajax: Ember.inject.service()
state: DS.attr() state: DS.attr()
number: DS.attr('number') number: DS.attr('number')
branch: DS.attr('string')
message: DS.attr('string') message: DS.attr('string')
_duration: DS.attr('number') _duration: DS.attr('number')
_config: DS.attr('object') _config: DS.attr('object')
_startedAt: DS.attr() _startedAt: DS.attr()
_finishedAt: DS.attr() _finishedAt: DS.attr('string')
pullRequest: DS.attr('boolean') pullRequest: DS.attr('boolean')
pullRequestTitle: DS.attr() pullRequestTitle: DS.attr()
pullRequestNumber: DS.attr('number') pullRequestNumber: DS.attr('number')
eventType: DS.attr('string') eventType: DS.attr('string')
repositoryId: DS.attr('number')
branch: DS.belongsTo('branch', async: false, inverse: 'builds')
repo: DS.belongsTo('repo', async: true) repo: DS.belongsTo('repo', async: true)
commit: DS.belongsTo('commit', async: true) commit: DS.belongsTo('commit', async: false)
jobs: DS.hasMany('job', async: true) jobs: DS.hasMany('job', async: true)
config: (-> config: (->
@ -91,11 +93,11 @@ Build = Model.extend DurationCalculations,
canRestart: Ember.computed.alias('isFinished') canRestart: Ember.computed.alias('isFinished')
cancel: (-> cancel: (->
Ajax.post "/builds/#{@get('id')}/cancel" @get('ajax').post "/builds/#{@get('id')}/cancel"
) )
restart: -> restart: ->
Ajax.post "/builds/#{@get('id')}/restart" @get('ajax').post "/builds/#{@get('id')}/restart"
formattedFinishedAt: (-> formattedFinishedAt: (->
if finishedAt = @get('finishedAt') if finishedAt = @get('finishedAt')

View File

@ -1,5 +1,4 @@
`import { durationFrom, configKeys, compact } from 'travis/utils/helpers'` `import { durationFrom, configKeys, compact } from 'travis/utils/helpers'`
`import Ajax from 'travis/utils/ajax'`
`import configKeysMap from 'travis/utils/keys-map'` `import configKeysMap from 'travis/utils/keys-map'`
`import Ember from 'ember'` `import Ember from 'ember'`
`import Model from 'travis/models/model'` `import Model from 'travis/models/model'`
@ -7,6 +6,7 @@
`import DurationCalculations from 'travis/utils/duration-calculations'` `import DurationCalculations from 'travis/utils/duration-calculations'`
Job = Model.extend DurationCalculations, Job = Model.extend DurationCalculations,
ajax: Ember.inject.service()
logId: DS.attr() logId: DS.attr()
queue: DS.attr() queue: DS.attr()
@ -30,7 +30,7 @@ Job = Model.extend DurationCalculations,
log: ( -> log: ( ->
@set('isLogAccessed', true) @set('isLogAccessed', true)
Log.create(job: this) Log.create(job: this, ajax: @get('ajax'))
).property() ).property()
startedAt: (-> startedAt: (->
@ -94,11 +94,11 @@ Job = Model.extend DurationCalculations,
canRestart: Ember.computed.alias('isFinished') canRestart: Ember.computed.alias('isFinished')
cancel: (-> cancel: (->
Ajax.post "/jobs/#{@get('id')}/cancel" @get('ajax').post "/jobs/#{@get('id')}/cancel"
) )
removeLog: -> removeLog: ->
Ajax.patch("/jobs/#{@get('id')}/log").then => @get('ajax').patch("/jobs/#{@get('id')}/log").then =>
@reloadLog() @reloadLog()
reloadLog: -> reloadLog: ->
@ -106,7 +106,7 @@ Job = Model.extend DurationCalculations,
@get('log').fetch() @get('log').fetch()
restart: -> restart: ->
Ajax.post "/jobs/#{@get('id')}/restart" @get('ajax').post "/jobs/#{@get('id')}/restart"
appendLog: (part) -> appendLog: (part) ->
@get('log').append part @get('log').append part

View File

@ -9,7 +9,7 @@ Request = Ember.Object.extend
accept: 'application/json; chunked=true; version=2, text/plain; version=2' accept: 'application/json; chunked=true; version=2, text/plain; version=2'
run: -> run: ->
Ajax.ajax "/jobs/#{@id}/log?cors_hax=true", 'GET', @get('ajax').ajax "/jobs/#{@id}/log?cors_hax=true", 'GET',
dataType: 'text' dataType: 'text'
headers: @HEADERS headers: @HEADERS
success: (body, status, xhr) => Ember.run(this, -> @handle(body, status, xhr)) success: (body, status, xhr) => Ember.run(this, -> @handle(body, status, xhr))
@ -50,7 +50,7 @@ Log = Ember.Object.extend
data['part_numbers'] = partNumbers if partNumbers data['part_numbers'] = partNumbers if partNumbers
data['after'] = after if after data['after'] = after if after
Ajax.ajax "/jobs/#{@get('job.id')}/log", 'GET', @get('ajax').ajax "/jobs/#{@get('job.id')}/log", 'GET',
dataType: 'json' dataType: 'json'
headers: headers:
accept: 'application/json; chunked=true; version=2' accept: 'application/json; chunked=true; version=2'
@ -81,7 +81,8 @@ Log = Ember.Object.extend
@set('removed', true) @set('removed', true)
@loadParts(json['log']['parts']) @loadParts(json['log']['parts'])
text: (text) => @loadText(text) text: (text) => @loadText(text)
Request.create(id: id, handlers: handlers, log: this).run() if id = @get('job.id') if id = @get('job.id')
Request.create(id: id, handlers: handlers, log: this, ajax: @get('ajax')).run()
clear: -> clear: ->
@clearParts() @clearParts()

View File

@ -1,40 +1,24 @@
`import ExpandableRecordArray from 'travis/utils/expandable-record-array'` `import ExpandableRecordArray from 'travis/utils/expandable-record-array'`
`import Model from 'travis/models/model'` `import Model from 'travis/models/model'`
`import Ajax from 'travis/utils/ajax'`
# TODO: Investigate for some weird reason if I use durationFrom here not durationFromHelper, # TODO: Investigate for some weird reason if I use durationFrom here not durationFromHelper,
# the function stops being visible inside computed properties. # the function stops being visible inside computed properties.
`import { durationFrom as durationFromHelper } from 'travis/utils/helpers'` `import { durationFrom as durationFromHelper } from 'travis/utils/helpers'`
`import Build from 'travis/models/build'` `import Build from 'travis/models/build'`
Repo = Model.extend Repo = Model.extend
ajax: Ember.inject.service()
slug: DS.attr() slug: DS.attr()
description: DS.attr() description: DS.attr()
private: DS.attr('boolean') private: DS.attr('boolean')
lastBuildNumber: DS.attr('number')
lastBuildState: DS.attr()
lastBuildStartedAt: DS.attr()
lastBuildFinishedAt: DS.attr()
githubLanguage: DS.attr() githubLanguage: DS.attr()
_lastBuildDuration: DS.attr('number')
lastBuildLanguage: DS.attr()
active: DS.attr() active: DS.attr()
lastBuildId: DS.attr('number')
lastBuildHash: (->
{
id: @get('lastBuildId')
number: @get('lastBuildNumber')
repo: this
}
).property('lastBuildId', 'lastBuildNumber')
lastBuild: (-> #lastBuild: DS.belongsTo('build')
if id = @get('lastBuildId') defaultBranch: DS.belongsTo('branch', async: false)
@store.find('build', id)
@store.recordForId('build', id)
).property('lastBuildId')
withLastBuild: -> withLastBuild: ->
@filter( (repo) -> repo.get('lastBuildId') ) @filter( (repo) -> repo.get('defaultBranch.lastBuild') )
sshKey: (-> sshKey: (->
@store.find('ssh_key', @get('id')) @store.find('ssh_key', @get('id'))
@ -51,7 +35,7 @@ Repo = Model.extend
builds: (-> builds: (->
id = @get('id') id = @get('id')
builds = @store.filter('build', event_type: ['push', 'api'], repository_id: id, (b) -> builds = @store.filter('build', event_type: ['push', 'api'], repository_id: id, (b) ->
b.get('repo.id') == id && (b.get('eventType') == 'push' || b.get('eventType') == 'api') b.get('repositoryId')+'' == id+'' && (b.get('eventType') == 'push' || b.get('eventType') == 'api')
) )
# TODO: move to controller # TODO: move to controller
@ -68,7 +52,7 @@ Repo = Model.extend
pullRequests: (-> pullRequests: (->
id = @get('id') id = @get('id')
builds = @store.filter('build', event_type: 'pull_request', repository_id: id, (b) -> builds = @store.filter('build', event_type: 'pull_request', repository_id: id, (b) ->
b.get('repo.id') == id && b.get('eventType') == 'pull_request' b.get('repositoryId')+'' == id+'' && b.get('eventType') == 'pull_request'
) )
# TODO: move to controller # TODO: move to controller
@ -85,7 +69,7 @@ Repo = Model.extend
).property() ).property()
branches: (-> branches: (->
builds = @store.find 'build', repository_id: @get('id'), branches: true builds = @store.query 'build', repository_id: @get('id'), branches: true
builds.then -> builds.then ->
builds.set 'isLoaded', true builds.set 'isLoaded', true
@ -101,27 +85,13 @@ Repo = Model.extend
(@get('slug') || '').split('/')[1] (@get('slug') || '').split('/')[1]
).property('slug') ).property('slug')
lastBuildDuration: (->
duration = @get('_lastBuildDuration')
duration = durationFromHelper(@get('lastBuildStartedAt'), @get('lastBuildFinishedAt')) unless duration
duration
).property('_lastBuildDuration', 'lastBuildStartedAt', 'lastBuildFinishedAt')
sortOrderForLandingPage: (-> sortOrderForLandingPage: (->
state = @get('lastBuildState') state = @get('defaultBranch.lastBuild.state')
if state != 'passed' && state != 'failed' if state != 'passed' && state != 'failed'
0 0
else else
parseInt(@get('lastBuildId')) parseInt(@get('defaultBranch.lastBuild.id'))
).property('lastBuildId', 'lastBuildState') ).property('defaultBranch.lastBuild.id', 'defaultBranch.lastBuild.state')
sortOrder: (->
# cuz sortAscending seems buggy when set to false
if lastBuildFinishedAt = @get('lastBuildFinishedAt')
- new Date(lastBuildFinishedAt).getTime()
else
- new Date('9999').getTime() - parseInt(@get('lastBuildId'))
).property('lastBuildFinishedAt', 'lastBuildId')
stats: (-> stats: (->
if @get('slug') if @get('slug')
@ -132,32 +102,41 @@ Repo = Model.extend
).property('slug') ).property('slug')
updateTimes: -> updateTimes: ->
@notifyPropertyChange 'lastBuildDuration' @notifyPropertyChange 'defaultBranch.lastBuild.duration'
regenerateKey: (options) -> regenerateKey: (options) ->
Ajax.ajax '/repos/' + @get('id') + '/key', 'post', options @get('ajax').ajax '/repos/' + @get('id') + '/key', 'post', options
fetchSettings: -> fetchSettings: ->
Ajax.ajax('/repos/' + @get('id') + '/settings', 'get', forceAuth: true).then (data) -> @get('ajax').ajax('/repos/' + @get('id') + '/settings', 'get', forceAuth: true).then (data) ->
data['settings'] data['settings']
saveSettings: (settings) -> saveSettings: (settings) ->
Ajax.ajax('/repos/' + @get('id') + '/settings', 'patch', data: { settings: settings }) @get('ajax').ajax('/repos/' + @get('id') + '/settings', 'patch', data: { settings: settings })
Repo.reopenClass Repo.reopenClass
recent: -> recent: ->
@find() @find()
accessibleBy: (store, login) -> accessibleBy: (store, reposIds) ->
repos = store.find('repo', { member: login, orderBy: 'name' }) # this fires only for authenticated users and with API v3 that means getting
# only repos of currently logged in owner, but in the future it would be
# nice to not use that as it may change in the future
repos = store.filter('repo', (repo) ->
reposIds.indexOf(parseInt(repo.get('id'))) != -1
)
repos.then () -> promise = new Ember.RSVP.Promise (resolve, reject) ->
repos.set('isLoaded', true) store.query('repo', { 'repository.active': 'true' }).then( ->
resolve(repos)
, ->
reject()
)
repos promise
search: (store, query) -> search: (store, query) ->
promise = store.find('repo', search: query, orderBy: 'name') promise = store.query('repo', search: query, orderBy: 'name')
result = Ember.ArrayProxy.create(content: []) result = Ember.ArrayProxy.create(content: [])
promise.then -> promise.then ->
@ -168,7 +147,7 @@ Repo.reopenClass
withLastBuild: (store) -> withLastBuild: (store) ->
repos = store.filter('repo', {}, (build) -> repos = store.filter('repo', {}, (build) ->
build.get('lastBuildId') build.get('defaultBranch.lastBuild')
) )
repos.then () -> repos.then () ->
@ -176,20 +155,24 @@ Repo.reopenClass
repos repos
bySlug: (store, slug) ->
# first check if there is a repo with a given slug already ordered
repos = store.all('repo').filterBy('slug', slug)
if repos.get('length') > 0
repos
else
store.find('repo', { slug: slug })
fetchBySlug: (store, slug) -> fetchBySlug: (store, slug) ->
repos = @bySlug(store, slug) repos = store.peekAll('repo').filterBy('slug', slug)
if repos.get('length') > 0 if repos.get('length') > 0
repos.get('firstObject') repos.get('firstObject')
else else
repos.then (repos) -> adapter = store.adapterFor('repo')
modelClass = store.modelFor('repo')
adapter.findRecord(store, modelClass, slug).then (payload) ->
serializer = store.serializerFor('repo')
modelClass = store.modelFor('repo')
result = serializer.normalizeResponse(store, modelClass, payload, null, 'findRecord')
repo = store.push(data: result.data)
for record in result.included
r = store.push(data: record)
repo
, ->
error = new Error('repo not found') error = new Error('repo not found')
error.slug = slug error.slug = slug
Ember.get(repos, 'firstObject') || throw(error) Ember.get(repos, 'firstObject') || throw(error)

View File

@ -1,9 +1,13 @@
`import Ember from 'ember'` `import Ember from 'ember'`
`import Model from 'travis/models/model'` `import Model from 'travis/models/model'`
`import Ajax from 'travis/utils/ajax'`
`import config from 'travis/config/environment'` `import config from 'travis/config/environment'`
User = Model.extend User = Model.extend
ajax: Ember.inject.service()
# TODO: this totally not should be needed here
sessionStorage: Ember.inject.service()
name: DS.attr() name: DS.attr()
email: DS.attr() email: DS.attr()
login: DS.attr() login: DS.attr()
@ -27,7 +31,7 @@ User = Model.extend
).property() ).property()
_rawPermissions: (-> _rawPermissions: (->
Ajax.get('/users/permissions') @get('ajax').get('/users/permissions')
).property() ).property()
permissions: (-> permissions: (->
@ -72,12 +76,12 @@ User = Model.extend
sync: -> sync: ->
self = this self = this
Ajax.post('/users/sync', {}, -> @get('ajax').post('/users/sync', {}, ->
self.setWithSession('isSyncing', true) self.setWithSession('isSyncing', true)
) )
poll: -> poll: ->
Ajax.get '/users', (data) => @get('ajax').get '/users', (data) =>
if data.user.is_syncing if data.user.is_syncing
self = this self = this
setTimeout -> setTimeout ->
@ -88,12 +92,12 @@ User = Model.extend
@setWithSession('syncedAt', data.user.synced_at) @setWithSession('syncedAt', data.user.synced_at)
Travis.trigger('user:synced', data.user) Travis.trigger('user:synced', data.user)
@store.findQuery('account', {}) @store.query('account', {})
setWithSession: (name, value) -> setWithSession: (name, value) ->
@set(name, value) @set(name, value)
user = JSON.parse(Travis.sessionStorage.getItem('travis.user')) user = JSON.parse(@get('sessionStorage').getItem('travis.user'))
user[name.underscore()] = @get(name) user[name.underscore()] = @get(name)
Travis.sessionStorage.setItem('travis.user', JSON.stringify(user)) @get('sessionStorage').setItem('travis.user', JSON.stringify(user))
`export default User` `export default User`

View File

@ -13,7 +13,7 @@ Router = Ember.Router.extend
# #
# we should probably think about a more general way to # we should probably think about a more general way to
# do this, location should not know about auth status # do this, location should not know about auth status
Location.create(auth: @container.lookup('auth:main')) Location.create(auth: @container.lookup('service:auth'))
).property() ).property()
# TODO: this is needed, because in the original version # TODO: this is needed, because in the original version

View File

@ -2,7 +2,7 @@
Route = TravisRoute.extend Route = TravisRoute.extend
model: -> model: ->
@store.find('account', { all: true }) @store.query('account', { all: true })
setupController: (controller, model) -> setupController: (controller, model) ->
user = model.filterBy('type', 'user')[0] user = model.filterBy('type', 'user')[0]

View File

@ -21,7 +21,7 @@ Route = TravisRoute.extend BuildFaviconMixin,
@get('stylesheetsManager').disable('dashboard') @get('stylesheetsManager').disable('dashboard')
if !config.pro if !config.pro
repos = @get('store').all('repo') repos = @get('store').peekAll('repo')
repos.forEach (repo) => repos.forEach (repo) =>
@subscribeToRepo(repo) @subscribeToRepo(repo)

View File

@ -1,8 +1,9 @@
`import Ember from 'ember'` `import Ember from 'ember'`
`import TravisRoute from 'travis/routes/basic'` `import TravisRoute from 'travis/routes/basic'`
`import Ajax from 'travis/utils/ajax'`
Route = TravisRoute.extend Route = TravisRoute.extend
ajax: Ember.inject.service()
needsAuth: true needsAuth: true
setupController: (controller) -> setupController: (controller) ->
@_super.apply this, arguments @_super.apply this, arguments
@ -10,7 +11,7 @@ Route = TravisRoute.extend
model: -> model: ->
repo = @modelFor('repo') repo = @modelFor('repo')
Ajax.get("/repos/#{repo.get('id')}/caches").then( (data) -> @get('ajax').get("/repos/#{repo.get('id')}/caches").then( (data) ->
caches = {} caches = {}
data["caches"].forEach (cacheData) -> data["caches"].forEach (cacheData) ->

View File

@ -15,7 +15,7 @@ Route = SimpleLayoutRoute.extend
if !controller.get('isSyncing') if !controller.get('isSyncing')
self = this self = this
Ember.run.later this, -> Ember.run.later this, ->
@store.find('repo', member: @get('controller.user.login')).then( (repos) -> @store.query('repo', member: @get('controller.user.login')).then( (repos) ->
if repos.get('length') if repos.get('length')
self.transitionTo('main') self.transitionTo('main')
else else

View File

@ -21,10 +21,10 @@ Route = BasicRoute.extend
@_super.apply this, arguments @_super.apply this, arguments
loadMoreRepos: -> loadMoreRepos: ->
@store.find('build').then (builds) => @store.findAll('build').then (builds) =>
repoIds = builds.mapBy('data.repo').uniq() repoIds = builds.mapBy('data.repo').uniq()
repos = @get('repos.repos') repos = @get('repos.repos')
@store.find('repo', ids: repoIds).then (reposFromRequest) => @store.query('repo', ids: repoIds).then (reposFromRequest) =>
reposFromRequest.toArray().forEach (repo) -> reposFromRequest.toArray().forEach (repo) ->
repos.pushObject(repo) unless repos.contains(repo) repos.pushObject(repo) unless repos.contains(repo)

View File

@ -18,16 +18,18 @@ Route = TravisRoute.extend
@controllerFor('job').set('job', model) @controllerFor('job').set('job', model)
repo.activate('job') repo.activate('job')
if build = model.get('build') buildController = @controllerFor('build')
build = @store.recordForId('build', build.get('id'))
buildController = @controllerFor('build')
# this is a hack to not set favicon changes from build model.get('repo')
# controller while we're viewing job, this should go away if buildPromise = model.get('build')
# after refactoring of controllers buildPromise.then (build) =>
buildController.set('sendFaviconStateChanges', false) build = @store.recordForId('build', build.get('id'))
buildController.set('build', build)
buildController.set('build', build) # this is a hack to not set favicon changes from build
# controller while we're viewing job, this should go away
# after refactoring of controllers
buildController.set('sendFaviconStateChanges', false)
model: (params) -> model: (params) ->
@store.find('job', params.job_id) @store.find('job', params.job_id)

View File

@ -12,12 +12,12 @@ Route = TravisRoute.extend
@controllerFor('repos').activate(@get('reposTabName')) @controllerFor('repos').activate(@get('reposTabName'))
@currentRepoDidChange() @currentRepoDidChange()
if repos = @controllerFor('repos').get('repos') if repos = @controllerFor('repos')
repos.addObserver('firstObject', this, 'currentRepoDidChange') repos.addObserver('repos.firstObject', this, 'currentRepoDidChange')
deactivate: -> deactivate: ->
if repos = @controllerFor('repos').get('repos') if repos = @controllerFor('repos')
repos.removeObserver('firstObject', this, 'currentRepoDidChange') repos.removeObserver('repos.firstObject', this, 'currentRepoDidChange')
@_super.apply(this, arguments) @_super.apply(this, arguments)

View File

@ -1,6 +1,5 @@
`import Ember from 'ember'` `import Ember from 'ember'`
`import TravisRoute from 'travis/routes/basic'` `import TravisRoute from 'travis/routes/basic'`
`import Ajax from 'travis/utils/ajax'`
`import config from 'travis/config/environment'` `import config from 'travis/config/environment'`
Route = TravisRoute.extend Route = TravisRoute.extend

View File

@ -1,6 +1,5 @@
`import Ember from 'ember'` `import Ember from 'ember'`
`import TravisRoute from 'travis/routes/basic'` `import TravisRoute from 'travis/routes/basic'`
`import Ajax from 'travis/utils/ajax'`
`import config from 'travis/config/environment'` `import config from 'travis/config/environment'`
Route = TravisRoute.extend Route = TravisRoute.extend

View File

@ -1,6 +1,5 @@
`import Ember from 'ember'` `import Ember from 'ember'`
`import TravisRoute from 'travis/routes/basic'` `import TravisRoute from 'travis/routes/basic'`
`import Ajax from 'travis/utils/ajax'`
`import config from 'travis/config/environment'` `import config from 'travis/config/environment'`
Route = TravisRoute.extend Route = TravisRoute.extend

View File

@ -1,7 +1,9 @@
`import TravisRoute from 'travis/routes/basic'` `import TravisRoute from 'travis/routes/basic'`
`import Repo from 'travis/models/repo'` `import Repo from 'travis/models/repo'`
`import Ember from 'ember'`
Route = TravisRoute.extend Route = TravisRoute.extend
store: Ember.inject.service()
titleToken: (model) -> titleToken: (model) ->
model.get('slug') model.get('slug')
@ -11,7 +13,7 @@ Route = TravisRoute.extend
setupController: (controller, model) -> setupController: (controller, model) ->
# TODO: if repo is just a data hash with id and slug load it # TODO: if repo is just a data hash with id and slug load it
# as incomplete record # as incomplete record
model = @store.find('repo', model.id) if model && !model.get model = @get('store').find('repo', model.id) if model && !model.get
controller.set('repo', model) controller.set('repo', model)
serialize: (repo) -> serialize: (repo) ->
@ -21,7 +23,7 @@ Route = TravisRoute.extend
model: (params) -> model: (params) ->
slug = "#{params.owner}/#{params.name}" slug = "#{params.owner}/#{params.name}"
Repo.fetchBySlug(@store, slug) Repo.fetchBySlug(@get('store'), slug)
resetController: -> resetController: ->
@controllerFor('repo').deactivate() @controllerFor('repo').deactivate()

View File

@ -6,7 +6,7 @@ Route = TravisRoute.extend
@controllerFor('repo').activate('current') @controllerFor('repo').activate('current')
renderTemplate: -> renderTemplate: ->
if @modelFor('repo').get('lastBuildId') if @modelFor('repo').get('defaultBranch.lastBuild')
@render 'build' @render 'build'
else else
@render 'builds/not_found' @render 'builds/not_found'

View File

@ -7,6 +7,6 @@ Route = TravisRoute.extend
@controllerFor('repo').activate('requests') @controllerFor('repo').activate('requests')
model: -> model: ->
@store.find 'request', repository_id: @modelFor('repo').get('id') @store.query 'request', repository_id: @modelFor('repo').get('id')
`export default Route` `export default Route`

View File

@ -1,8 +1,9 @@
`import TravisRoute from 'travis/routes/basic'` `import TravisRoute from 'travis/routes/basic'`
`import Ajax from 'travis/utils/ajax'`
`import config from 'travis/config/environment'` `import config from 'travis/config/environment'`
Route = TravisRoute.extend Route = TravisRoute.extend
ajax: Ember.inject.service()
needsAuth: true needsAuth: true
setupController: (controller, model) -> setupController: (controller, model) ->
@_super.apply(this, arguments) @_super.apply(this, arguments)
@ -27,7 +28,7 @@ Route = TravisRoute.extend
fetchSshKey: () -> fetchSshKey: () ->
repo = @modelFor('repo') repo = @modelFor('repo')
Ajax.get "/repos/#{repo.get('id')}/key", (data) => @get('ajax').get "/repos/#{repo.get('id')}/key", (data) =>
Ember.Object.create(fingerprint: data.fingerprint) Ember.Object.create(fingerprint: data.fingerprint)
fetchRepositoryActiveFlag: -> fetchRepositoryActiveFlag: ->

View File

@ -1,8 +1,9 @@
`import Ember from 'ember'` `import Ember from 'ember'`
`import Ajax from 'travis/utils/ajax'`
`import TravisRoute from 'travis/routes/basic'` `import TravisRoute from 'travis/routes/basic'`
Route = TravisRoute.extend Route = TravisRoute.extend
ajax: Ember.inject.service()
titleToken: 'Ssh Keys' titleToken: 'Ssh Keys'
model: (params) -> model: (params) ->
@ -17,7 +18,7 @@ Route = TravisRoute.extend
afterModel: (model, transition) -> afterModel: (model, transition) ->
repo = @modelFor('repo') repo = @modelFor('repo')
Ajax.get "/repos/#{repo.get('id')}/key", (data) => @get('ajax').get "/repos/#{repo.get('id')}/key", (data) =>
@defaultKey = Ember.Object.create(fingerprint: data.fingerprint) @defaultKey = Ember.Object.create(fingerprint: data.fingerprint)
setupController: (controller, model) -> setupController: (controller, model) ->

View File

@ -1,7 +1,7 @@
`import DS from 'ember-data'` `import DS from 'ember-data'`
`import V2FallbackSerializer from 'travis/serializers/v2_fallback'`
Serializer = DS.ActiveModelSerializer.extend Serializer = V2FallbackSerializer.extend
defaultSerializer: 'application' isNewSerializerAPI: true
serializer: 'application'
`export default Serializer` `export default Serializer`

12
app/serializers/branch.js Normal file
View File

@ -0,0 +1,12 @@
import Ember from 'ember';
import V3Serializer from 'travis/serializers/v3';
export default V3Serializer.extend({
extractAttributes(klass, payload) {
payload.id = payload['@href'];
return this._super(...arguments);
},
extractId(modelClass, resourceHash) {
return resourceHash.id || resourceHash['@href'];
}
});

View File

@ -1,19 +0,0 @@
`import Ember from 'ember'`
`import ApplicationSerializer from 'travis/serializers/application'`
Serializer = ApplicationSerializer.extend
attrs: {
repo: { key: 'repository_id' }
_config: { key: 'config' }
_finishedAt: { key: 'finished_at' }
_startedAt: { key: 'started_at' }
_duration: { key: 'duration' }
}
extractSingle: (store, primaryType, rawPayload, recordId) ->
if commit = rawPayload.commit
rawPayload.commits = [commit]
@_super(store, primaryType, rawPayload, recordId)
`export default Serializer`

93
app/serializers/build.js Normal file
View File

@ -0,0 +1,93 @@
import Ember from 'ember';
import V2FallbackSerializer from 'travis/serializers/v2_fallback';
var Serializer = V2FallbackSerializer.extend({
isNewSerializerAPI: true,
attrs: {
_config: {
key: 'config'
},
_finished_at: {
key: 'finished_at'
},
_started_at: {
key: 'started_at'
},
_duration: {
key: 'duration'
}
},
extractRelationships: function(modelClass, resourceHash) {
var result;
result = this._super(modelClass, resourceHash);
return result;
},
normalizeArrayResponse: function(store, primaryModelClass, payload, id, requestType) {
var result;
if (payload.commits) {
payload.builds.forEach(function(build) {
var commit, commit_id;
commit_id = build.commit_id;
if (commit = payload.commits.findBy('id', commit_id)) {
build.commit = commit;
return delete build.commit_id;
}
});
}
return this._super.apply(this, arguments);
},
keyForV2Relationship: function(key, typeClass, method) {
if(key === 'jobs') {
return 'job_ids';
} else if (key === 'repo') {
return 'repository_id';
} else if (key === 'commit') {
return key;
} else {
return this._super.apply(this, arguments);
}
},
keyForRelationship(key, typeClass, method) {
if (key === 'repo') {
return 'repository';
} else {
return this._super.apply(this, arguments);
}
},
normalize: function(modelClass, resourceHash) {
var data, href, id, repoId, result;
// TODO: remove this after switching to V3 entirely
if(!resourceHash['@type']) {
let build = resourceHash.build,
commit = resourceHash.commit;
let branch = {
name: commit.branch,
default_branch: build.is_on_default_branch,
"@href": `/repo/${build.repository_id}/branch/${commit.branch}`
};
resourceHash.build.branch = branch;
}
result = this._super(modelClass, resourceHash);
data = result.data;
if (repoId = resourceHash.repository_id) {
data.attributes.repositoryId = repoId;
} else if (resourceHash.repository) {
if (href = resourceHash.repository['@href']) {
id = href.match(/\d+/)[0];
data.attributes.repositoryId = id;
}
}
return result;
}
});
export default Serializer;

View File

@ -1,18 +1,30 @@
`import Ember from 'ember'` `import Ember from 'ember'`
`import ApplicationSerializer from 'travis/serializers/application'` `import V2FallbackSerializer from 'travis/serializers/v2_fallback'`
Serializer = ApplicationSerializer.extend Serializer = V2FallbackSerializer.extend
isNewSerializerAPI: true
attrs: { attrs: {
repo: { key: 'repository_id' }
_config: { key: 'config' } _config: { key: 'config' }
_finishedAt: { key: 'finished_at' } _finished_at: { key: 'finished_at' }
_startedAt: { key: 'started_at' } _started_at: { key: 'started_at' }
} }
extractSingle: (store, primaryType, rawPayload, recordId) -> keyForV2Relationship: (key, typeClass, method) ->
if commit = rawPayload.commit if key == 'repo'
rawPayload.commits = [commit] 'repository'
else
@_super.apply(this, arguments)
@_super(store, primaryType, rawPayload, recordId) keyForV2Relationship: (key, typeClass, method) ->
if key == 'repo'
'repository_id'
else
@_super.apply(this, arguments)
normalize: (modelClass, resourceHash) ->
if resourceHash.commit
resourceHash.commit['type'] = 'commit'
@_super(modelClass, resourceHash)
`export default Serializer` `export default Serializer`

View File

@ -1,9 +0,0 @@
`import Ember from 'ember'`
`import ApplicationSerializer from 'travis/serializers/application'`
Serializer = ApplicationSerializer.extend
attrs: {
_lastBuildDuration: { key: 'last_build_duration' }
}
`export default Serializer`

16
app/serializers/repo.js Normal file
View File

@ -0,0 +1,16 @@
import Ember from 'ember';
import V2FallbackSerializer from 'travis/serializers/v2_fallback';
var Serializer = V2FallbackSerializer.extend({
isNewSerializerAPI: true,
normalizeResponse(store, primaryModelClass, payload, id, requestType) {
if(!id && requestType == 'findRecord') {
id = payload.id;
}
return this._super(store, primaryModelClass, payload, id, requestType);
}
});
export default Serializer;

View File

@ -3,8 +3,8 @@
Serializer = ApplicationSerializer.extend Serializer = ApplicationSerializer.extend
attrs: { attrs: {
branchName: { key: 'branch' } branch_name: { key: 'branch' }
tagName: { key: 'tag' } tag_name: { key: 'tag' }
repo: { key: 'repository_id' } repo: { key: 'repository_id' }
} }

View File

@ -0,0 +1,93 @@
import Ember from 'ember';
import V3Serializer from 'travis/serializers/v3';
export default V3Serializer.extend({
isNewSerializerAPI: true,
extractRelationships(modelClass, resourceHash) {
if(resourceHash['@type']) {
return this._super(...arguments);
} else {
let relationships = {};
modelClass.eachRelationship((key, relationshipMeta) => {
// V2 API payload
let relationship = null;
let relationshipKey = this.keyForV2Relationship(key, relationshipMeta.kind, 'deserialize');
if (resourceHash.hasOwnProperty(key) || resourceHash.hasOwnProperty(relationshipKey)) {
let data = null;
let relationshipHash = resourceHash[key] || resourceHash[relationshipKey];
if (relationshipMeta.kind === 'belongsTo') {
data = this.extractRelationship(relationshipMeta.type, relationshipHash);
} else if (relationshipMeta.kind === 'hasMany') {
data = relationshipHash.map((item) => this.extractRelationship(relationshipMeta.type, item));
}
relationship = { data };
}
if (relationship) {
relationships[key] = relationship;
}
});
return relationships;
}
},
normalize(modelClass, resourceHash) {
if(resourceHash['@type']) {
return this._super(...arguments);
} else {
var modelKey = modelClass.modelName;
var attributes = resourceHash[modelKey];
if(attributes) {
for(var key in attributes) {
resourceHash[key] = attributes[key];
}
resourceHash['type'] = modelKey;
delete resourceHash[modelKey];
}
let { data, included } = this._super(...arguments);
if(!included) {
included = [];
}
let store = this.store;
if(data.relationships) {
Object.keys(data.relationships).forEach(function (key) {
let relationship = data.relationships[key];
let process = function(data) {
if(Object.keys(data).sort()+'' !== 'id,type' || (data['@href'] && data.type == 'branch')) {
// no need to add records if they have only id and type
let type = key.singularize();
let serializer = store.serializerFor(type);
let modelClass = store.modelFor(type);
let normalized = serializer.normalize(modelClass, data);
included.push(normalized.data);
if(normalized.included) {
normalized.included.forEach(function(item) {
included.push(item);
});
}
}
};
if(Array.isArray(relationship.data)) {
relationship.data.forEach(process);
} else if(relationship && relationship.data) {
process(relationship.data);
}
});
}
return { data, included };
}
},
keyForV2Relationship(key, typeClass, method) {
return key.underscore() + '_id';
}
});

186
app/serializers/v3.js Normal file
View File

@ -0,0 +1,186 @@
import Ember from 'ember';
import DS from 'ember-data';
var traverse = function(object, callback) {
if(!object) {
return;
}
if(typeof(object) === 'object' && !Ember.isArray(object)) {
callback(object);
}
if(Ember.isArray(object)) {
for(let item of object) {
traverse(item, callback);
}
} else if(typeof object === 'object') {
for(let key in object) {
if(object.hasOwnProperty(key)) {
let item = object[key];
traverse(item, callback);
}
}
}
};
export default DS.JSONSerializer.extend({
isNewSerializerAPI: true,
extractRelationship(type, hash) {
if(!hash.id && hash['@href']) {
hash.id = hash['@href'];
}
let relationshipHash = this._super(...arguments);
if(relationshipHash && relationshipHash['@type']) {
relationshipHash.type = relationshipHash['@type'];
} else if(relationshipHash && !relationshipHash.type) {
relationshipHash.type = type;
}
return relationshipHash;
},
extractRelationships() {
let relationships = this._super(...arguments);
return relationships;
},
keyForRelationship(key, typeClass, method) {
if(key && key.underscore) {
return key.underscore();
} else {
return key;
}
},
extractAttributes(modelClass, resourceHash) {
let attributes = this._super(...arguments);
for(let key in attributes) {
if(key.startsWith('@')) {
delete attributes.key;
}
}
return attributes;
},
normalizeResponse(store, primaryModelClass, payload, id, requestType) {
this._fixReferences(payload);
return this._super(...arguments);
},
normalizeArrayResponse(store, primaryModelClass, payload, id, requestType) {
let documentHash = {
data: null,
included: []
};
let meta = this.extractMeta(store, primaryModelClass, payload);
if (meta) {
Ember.assert('The `meta` returned from `extractMeta` has to be an object, not "' + Ember.typeOf(meta) + '".', Ember.typeOf(meta) === 'object');
documentHash.meta = meta;
}
let items, type;
if(type = payload['@type']) {
items = payload[type];
} else {
items = payload[primaryModelClass.modelName.underscore() + 's'];
}
documentHash.data = items.map((item) => {
let { data, included } = this.normalize(primaryModelClass, item);
if (included) {
documentHash.included.push(...included);
}
return data;
});
return documentHash;
},
normalize(modelClass, resourceHash) {
let { data, included } = this._super(...arguments);
if(!included) {
included = [];
}
let store = this.store;
if(data.relationships) {
Object.keys(data.relationships).forEach(function (key) {
let relationship = data.relationships[key];
let process = function(data) {
if(data['@representation'] !== 'standard') {
return;
}
let type = data['@type'];
let serializer = store.serializerFor(type);
let modelClass = store.modelFor(type);
let normalized = serializer.normalize(modelClass, data);
included.push(normalized.data);
if(normalized.included) {
normalized.included.forEach(function(item) {
included.push(item);
});
}
};
if(Array.isArray(relationship.data)) {
relationship.data.forEach(process);
} else if(relationship && relationship.data) {
process(relationship.data);
}
});
}
return { data, included };
},
keyForAttribute(key, method) {
if(method === 'deserialize') {
return Ember.String.underscore(key);
} else {
return Ember.String.camelize(key);
}
},
_fixReferences(payload) {
let byHref = {}, href, records;
if(payload['@type']) {
// API V3 doesn't return all of the objects in a full representation
// If an object is present in one place in the response, all of the
// other occurences will be just references of a kind - they will just
// include @href property.
//
// I don't want to identify records by href in ember-data, so here I'll
// set an id and a @type field on all of the references.
//
// First we need to group all of the items in the response by href:
traverse(payload, (item) => {
if(href = item['@href']) {
if(records = byHref[href]) {
records.push(item);
} else {
byHref[href] = [item];
}
}
});
// Then we can choose a record with an id for each href and put the id
// in all of the other occurences.
for(let href in byHref) {
records = byHref[href];
let recordWithAnId = records.find( (record) => record.id );
if(recordWithAnId) {
for(let record of records) {
record.id = recordWithAnId.id;
//record['@type'] = recordWithAnId['@type'];
}
}
}
}
return payload;
}
});

157
app/services/ajax.coffee Normal file
View File

@ -0,0 +1,157 @@
`import Ember from 'ember';`
`import config from 'travis/config/environment'`
jQuery.support.cors = true
default_options =
accepts:
json: 'application/json; version=2'
Ajax = Ember.Service.extend
auth: Ember.inject.service()
publicEndpoints: [/\/repos\/?.*/, /\/builds\/?.*/, /\/jobs\/?.*/]
privateEndpoints: [/\/repos\/\d+\/caches/]
get: (url, callback, errorCallback) ->
@ajax(url, 'get', success: callback, error: errorCallback)
post: (url, data, callback) ->
@ajax(url, 'post', data: data, success: callback)
patch: (url, data, callback) ->
@ajax(url, 'patch', data: data, success: callback)
needsAuth: (method, url) ->
return true if config.pro
return true if method != 'GET'
publicEndpoint = @publicEndpoints.find (pattern) ->
url.match(pattern)
privateEndpoint = @privateEndpoints.find (pattern) ->
url.match(pattern)
!publicEndpoint || privateEndpoint
ajax: (url, method, options) ->
# TODO: we have our own ajax implementation, because jQuery didn't
# properly worked with headers on firefox, it would be nice to check
# if this is still a problem and if we can remove this
method = method || "GET"
method = method.toUpperCase()
endpoint = config.apiEndpoint || ''
options = options || {}
token = Ember.get(this, 'auth').token()
if token && (@needsAuth(method, url) || options.forceAuth)
options.headers ||= {}
options.headers['Authorization'] ||= "token #{token}"
options.url = url = "#{endpoint}#{url}"
options.type = method
options.dataType = options.dataType || 'json'
options.context = this
if options.data && method != 'GET'
options.data = JSON.stringify(options.data)
if method != 'GET' && method != 'HEAD'
options.contentType = options.contentType || 'application/json; charset=utf-8'
success = options.success || (->)
options.success = (data, status, xhr) =>
Travis.lookup('controller:flash').loadFlashes(data.flash) if data?.flash
delete data.flash if data?
success.apply(this, arguments)
error = options.error || (->)
options.error = (data, status, xhr) =>
Travis.lookup('controller:flash').pushObject(data.flash) if data?.flash
delete data.flash if data?
error.apply(this, arguments)
options = $.extend(options, default_options)
if testMode?
console.log('Running ajax with', options.url)
# we use jquery.mockjax for test, I don't want to hack it or rewrite it,
# so let's just pretend we still use ajax if testing mode is on
return new Ember.RSVP.Promise( (resolve, reject) ->
oldSuccess = options.success
options.success = (json, status, xhr) ->
Ember.run this, ->
oldSuccess.call(this, json, status, xhr)
Ember.run(null, resolve, json)
oldError = options.error
options.error = (jqXHR) ->
if jqXHR
# for a context, please see https://github.com/emberjs/ember.js/issues/3051
jqXHR.then = null
Ember.run this, ->
oldError.call this, jqXHR
reject(jqXHR)
$.ajax(options)
)
if options.data && (method == "GET" || method == "HEAD")
params = jQuery.param(options.data)
delimeter = if url.indexOf('?') == -1 then '?' else '&'
url = url + delimeter + params
xhr = new XMLHttpRequest()
xhr.open(method, url)
if options.accepts && !options.headers?.accept?
accepts = []
for key, value of options.accepts
accepts.pushObject(value)
xhr.setRequestHeader('Accept', accepts.join(', '))
if options.headers
for name, value of options.headers
xhr.setRequestHeader(name, value)
if options.contentType
xhr.setRequestHeader('Content-Type', options.contentType)
resolve = null
reject = null
promise = new Ember.RSVP.Promise( (_resolve, _reject) ->
resolve = _resolve
reject = _reject
)
xhr.onreadystatechange = ->
if xhr.readyState == 4
contentType = xhr.getResponseHeader('Content-Type')
data = if contentType && contentType.match /application\/json/
try
jQuery.parseJSON(xhr.responseText)
catch e
console.log('error while parsing a response', method, options.url, xhr.responseText)
else
xhr.responseText
if xhr.status >= 200 && xhr.status < 300
resolve(data)
options.success.call(options.context, data, xhr.status, xhr)
else
reject(xhr)
options.error.call(data, xhr.status, xhr)
data = options.data
if typeof(options.data) == "object" && (Ember.isNone(options.contentType) || options.contentType.match /application\/json/)
data = JSON.stringify(data)
xhr.send(data)
return promise
`export default Ajax`

View File

@ -1,7 +1,11 @@
`import config from 'travis/config/environment'` `import config from 'travis/config/environment'`
`import Ajax from 'travis/utils/ajax'`
Auth = Ember.Object.extend Auth = Ember.Service.extend
store: Ember.inject.service(),
storage: Ember.inject.service(),
sessionStorage: Ember.inject.service(),
ajax: Ember.inject.service()
state: "signed-out" state: "signed-out"
receivingEnd: "#{location.protocol}//#{location.host}" receivingEnd: "#{location.protocol}//#{location.host}"
@ -9,20 +13,20 @@ Auth = Ember.Object.extend
window.addEventListener('message', (e) => @receiveMessage(e)) window.addEventListener('message', (e) => @receiveMessage(e))
token: -> token: ->
Travis.sessionStorage.getItem('travis.token') @get('sessionStorage').getItem('travis.token')
endpoint: (-> endpoint: (->
config.apiEndpoint config.apiEndpoint
).property(), ).property(),
signOut: -> signOut: ->
@storage.removeItem('travis.user') @get('storage').removeItem('travis.user')
@storage.removeItem('travis.token') @get('storage').removeItem('travis.token')
@sessionStorage.clear() @get('sessionStorage').clear()
@set('state', 'signed-out') @set('state', 'signed-out')
@set('user', undefined) @set('user', undefined)
if user = @get('currentUser') if user = @get('currentUser')
@store.unloadAll('user') @get('store').unloadAll('user')
@set('currentUser', null) @set('currentUser', null)
@sendToApp('afterSignOut') @sendToApp('afterSignOut')
Travis.trigger('user:signed_out') Travis.trigger('user:signed_out')
@ -36,7 +40,7 @@ Auth = Ember.Object.extend
$('<iframe id="auth-frame" />').hide().appendTo('body').attr('src', url) $('<iframe id="auth-frame" />').hide().appendTo('body').attr('src', url)
autoSignIn: (data) -> autoSignIn: (data) ->
data ||= @userDataFrom(@sessionStorage) || @userDataFrom(@storage) data ||= @userDataFrom(@get('sessionStorage')) || @userDataFrom(@get('storage'))
@setData(data) if data @setData(data) if data
userDataFrom: (storage) -> userDataFrom: (storage) ->
@ -54,7 +58,7 @@ Auth = Ember.Object.extend
validateUser: (user) -> validateUser: (user) ->
fieldsToValidate = ['id', 'login', 'token'] fieldsToValidate = ['id', 'login', 'token']
isTravisBecome = sessionStorage.getItem('travis.become') isTravisBecome = @get('sessionStorage').getItem('travis.become')
unless isTravisBecome unless isTravisBecome
fieldsToValidate.push 'correct_scopes' fieldsToValidate.push 'correct_scopes'
@ -72,8 +76,8 @@ Auth = Ember.Object.extend
false false
setData: (data) -> setData: (data) ->
@storeData(data, @sessionStorage) @storeData(data, @get('sessionStorage'))
@storeData(data, @storage) unless @userDataFrom(@storage) @storeData(data, @get('storage')) unless @userDataFrom(@get('storage'))
user = @loadUser(data.user) user = @loadUser(data.user)
@set('currentUser', user) @set('currentUser', user)
@ -83,19 +87,19 @@ Auth = Ember.Object.extend
refreshUserData: (user) -> refreshUserData: (user) ->
unless user unless user
if data = @userDataFrom(@sessionStorage) || @userDataFrom(@storage) if data = @userDataFrom(@get('sessionStorage')) || @userDataFrom(@get('storage'))
user = data.user user = data.user
if user if user
Ajax.get("/users/#{user.id}").then (data) => @get('ajax').get("/users/#{user.id}").then (data) =>
if data.user.correct_scopes if data.user.correct_scopes
userRecord = @loadUser(data.user) userRecord = @loadUser(data.user)
userRecord.get('permissions') userRecord.get('permissions')
# if user is still signed in, update saved data # if user is still signed in, update saved data
if @get('signedIn') if @get('signedIn')
data.user.token = user.token data.user.token = user.token
@storeData(data, @sessionStorage) @storeData(data, @get('sessionStorage'))
@storeData(data, @storage) @storeData(data, @get('storage'))
Travis.trigger('user:refreshed', data.user) Travis.trigger('user:refreshed', data.user)
else else
return Ember.RSVP.Promise.reject() return Ember.RSVP.Promise.reject()
@ -119,8 +123,13 @@ Auth = Ember.Object.extend
storage.setItem('travis.user', JSON.stringify(data.user)) storage.setItem('travis.user', JSON.stringify(data.user))
loadUser: (user) -> loadUser: (user) ->
@store.pushPayload(users: [user]) @get('store').push(
@store.recordForId('user', user.id) data:
type: 'user',
id: user.id
attributes: user
)
@get('store').recordForId('user', user.id)
receiveMessage: (event) -> receiveMessage: (event) ->
if event.origin == @expectedOrigin() if event.origin == @expectedOrigin()

13
app/services/keyboard.js Normal file
View File

@ -0,0 +1,13 @@
import Ember from 'ember';
let KeyboardService = Ember.Service.extend({
bind(keyName, scope, callback) {
key(...arguments);
},
setScope(scope) {
key.setScope(scope);
}
});
export default KeyboardService;

View File

@ -0,0 +1,18 @@
`import Ember from 'ember'`
`import StorageService from 'travis/services/storage'`
`import Storage from 'travis/utils/hash-storage'`
SessionStorageService = StorageService.extend
init: ->
storage = null
try
# firefox will not throw error on access for sessionStorage var,
# you need to actually get something from session
window.sessionStorage.getItem('foo')
storage = window.sessionStorage
catch err
storage = Storage.create()
@set('storage', storage)
`export default SessionStorageService`

View File

@ -0,0 +1,22 @@
`import Ember from 'ember'`
`import Storage from 'travis/utils/hash-storage'`
StorageService = Ember.Service.extend
init: ->
storage = null
try
storage = window.localStorage || throw('no storage')
catch err
storage = Storage.create()
@set('storage', storage)
getItem: (key) ->
return @get("storage").getItem(key)
setItem: (key, value) ->
return @get("storage").setItem(key, value)
removeItem: (key) ->
return @get("storage").removeItem(key)
clear: ->
return @get("storage").clear()
`export default StorageService`

View File

@ -2,6 +2,8 @@
`import config from 'travis/config/environment'` `import config from 'travis/config/environment'`
Store = DS.Store.extend Store = DS.Store.extend
auth: Ember.inject.service()
defaultAdapter: 'application' defaultAdapter: 'application'
adapter: 'application' adapter: 'application'
@ -17,7 +19,8 @@ Store = DS.Store.extend
canHandleEvent: (event, data) -> canHandleEvent: (event, data) ->
[name, type] = event.split(':') [name, type] = event.split(':')
auth = @container.lookup('auth:main') auth = @get('auth')
if event != 'job:log' && auth.get('signedIn') && if event != 'job:log' && auth.get('signedIn') &&
!config.pro && !config.enterprise !config.pro && !config.enterprise
# if recent repos hasn't been opened yet, we can safely # if recent repos hasn't been opened yet, we can safely
@ -45,7 +48,7 @@ Store = DS.Store.extend
return unless @canHandleEvent(event, data) return unless @canHandleEvent(event, data)
if name == 'job' && data.job?.commit if name == 'job' && data.job?.commit
@pushPayload(commits: [data.job.commit]) @push(this.normalize('commit', data.job.commit))
if name == 'build' && data.build?.commit if name == 'build' && data.build?.commit
# TODO: commit should be a sideload record on build, not mixed with it # TODO: commit should be a sideload record on build, not mixed with it
@ -64,27 +67,25 @@ Store = DS.Store.extend
} }
delete(data.build.commit) delete(data.build.commit)
@pushPayload(commits: [commit]) @push(this.normalize('commit', commit))
if event == 'job:log' if event == 'job:log'
data = data.job data = data.job
job = @recordForId('job', data.id) job = @recordForId('job', data.id)
job.appendLog(number: parseInt(data.number), content: data._log, final: data.final) job.appendLog(number: parseInt(data.number), content: data._log, final: data.final)
else if data[name] else if data[name]
@_loadOne(name, data) @loadOne(name, data)
else else
throw "can't load data for #{name}" unless type throw "can't load data for #{name}" unless type
_loadOne: (type, json) -> loadOne: (type, json) ->
payload = {} record = @push(this.normalize(type, json))
payload[type.pluralize()] = [json[type]]
@pushPayload(payload)
# we get other types of records only in a few situations and # we get other types of records only in a few situations and
# it's not always needed to update data, so I'm specyfing which # it's not always needed to update data, so I'm specyfing which
# things I want to update here: # things I want to update here:
if type == 'build' && (json.repository || json.repo) if type == 'build' && (json.repository || json.repo)
data = json.repository || json.repo data = json.repository || json.repo
@pushPayload(repos: [data]) @push(this.normalize('repo', data))
`export default Store` `export default Store`

View File

@ -66,3 +66,4 @@
@import "app/layouts/missing-notice"; @import "app/layouts/missing-notice";
@import "app/layouts/settings"; @import "app/layouts/settings";
@import "fuzzy";

35
app/styles/fuzzy.scss Normal file
View File

@ -0,0 +1,35 @@
.travis-cmd {
opacity: 0.3;
background: #000;
width: 100%;
height: 100%;
z-index: 10;
top: 0;
left: 0;
position: fixed;
display: none;
.content {
opacity: 1;
}
&.visible {
display: block;
}
.content {
width: 400px;
min-height: 60px;
background-color: white;
border-radius: 10px;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.content, .input {
color: black;
font-size: 20px;
}
}

View File

@ -1 +1,3 @@
{{outlet}} {{outlet}}
{{travis-cmd}}

View File

@ -13,7 +13,7 @@
<small class="repo-main-branch" title={{build.pullRequestTitle}}>Pull Request #{{build.pullRequestNumber}}</small> <small class="repo-main-branch" title={{build.pullRequestTitle}}>Pull Request #{{build.pullRequestNumber}}</small>
{{build.pullRequestTitle}} {{build.pullRequestTitle}}
{{else}} {{else}}
<small class="repo-main-branch" title={{build.commit.branch}}>{{build.commit.branch}}</small> <small class="repo-main-branch" title={{build.branch.name}}>{{build.branch.name}}</small>
{{format-message build.commit.subject repo=build.repo}} {{format-message build.commit.subject repo=build.repo}}
{{/if}} {{/if}}
</h2> </h2>

View File

@ -1,13 +1,12 @@
<div class="tile tile--sidebar {{repo.lastBuildState}}"> <div class="tile tile--sidebar {{repo.defaultBranch.lastBuild.state}}">
<h2 class="tile-title"> <h2 class="tile-title">
{{#if repo.slug}} {{#if repo.slug}}
{{status-icon status=repo.lastBuildState}} {{status-icon status=repo.defaultBranch.lastBuild.state}}
{{#link-to "repo" repo class="slug"}}{{repo.slug}}{{/link-to}} {{#link-to "repo" repo class="slug"}}{{repo.slug}}{{/link-to}}
{{/if}} {{/if}}
</h2> </h2>
{{#with repo.lastBuildHash as lastBuild}} {{#if repo.slug}}
{{#if repo.slug}} {{#if repo.defaultBranch.lastBuild.id}}
{{#if lastBuild.id}}
<p class="tile-title float-right"> <p class="tile-title float-right">
<span class="icon"> <span class="icon">
<svg version="1.1" id="Layer_1" x="0px" y="0px" <svg version="1.1" id="Layer_1" x="0px" y="0px"
@ -21,12 +20,11 @@
L-277.2,202.4z"/> L-277.2,202.4z"/>
</svg> </svg>
</span> </span>
{{#link-to "build" repo lastBuild.id {{#link-to "build" repo repo.defaultBranch.lastBuild.id
class="last_build"}}{{lastBuild.number}}{{/link-to}} class="last_build"}}{{repo.defaultBranch.lastBuild.number}}{{/link-to}}
</p> </p>
{{/if}}
{{/if}} {{/if}}
{{/with}} {{/if}}
<p> <p>
<span class="icon"> <span class="icon">
@ -41,8 +39,8 @@
</svg> </svg>
</span> </span>
Duration: Duration:
<abbr class="duration" title={{lastBuildStartedAt}}> <abbr class="duration" title={{repo.defaultBranch.lastBuild.startedAt}}>
{{format-duration repo.lastBuildDuration}} {{format-duration repo.defaultBranch.lastBuild.duration}}
</abbr> </abbr>
</p> </p>
@ -64,8 +62,8 @@
</svg> </svg>
</span> </span>
Finished: Finished:
<abbr class="finished_at timeago" title={{lastBuildFinishedAt}}> <abbr class="finished_at timeago" title={{repo.defaultBranch.lastBuild.finishedAt}}>
{{format-time repo.lastBuildFinishedAt}} {{format-time repo.defaultBranch.lastBuild.finishedAt}}
</abbr> </abbr>
</p> </p>
</div> </div>

View File

@ -1,7 +1,5 @@
{{#each repos as |repo|}} {{#each sortedRepos as |repo|}}
{{repos-list-item repo=repo selectedRepo=selectedRepo}} {{repos-list-item repo=repo selectedRepo=selectedRepo}}
{{else}} {{else}}
<p class="empty">{{noReposMessage}}</p> <p class="empty">{{noReposMessage}}</p>
{{/each}} {{/each}}

View File

@ -0,0 +1,15 @@
<div class="content">
{{#if loading}}
{{loading-indicator}}
{{else}}
<input type="text" class="input" value={{filterString}}
oninput={{action "filterChanged" value="target.value"}}
onkeydown={{action "keypress"}}>
<div class="matches">
{{{matches}}}
</div>
{{/if}}
</div>

View File

@ -8,7 +8,7 @@
<div class="tile-main medium-8 columns"> <div class="tile-main medium-8 columns">
<h2 class="repo-main-commit"> <h2 class="repo-main-commit">
<small class="repo-main-branch" title={{job.commit.branch}}>{{job.commit.branch}}</small> <small class="repo-main-branch" title={{job.build.branch.name}}>{{job.build.branch.name}}</small>
{{format-message job.commit.subject repoBinding=job.repo}} {{format-message job.commit.subject repoBinding=job.repo}}
</h2> </h2>
<div class="repo-main-description"> <div class="repo-main-description">

View File

@ -26,7 +26,7 @@
{{#if repo.active}} {{#if repo.active}}
{{outlet}} {{outlet}}
{{else}} {{else}}
{{#if repo.lastBuildId}} {{#if repo.defaultBranch.lastBuild.id}}
{{outlet}} {{outlet}}
{{else}} {{else}}
{{not-active user=currentUser repo=repo}} {{not-active user=currentUser repo=repo}}

View File

@ -1,155 +1 @@
`import Ember from 'ember';`
`import config from 'travis/config/environment'`
jQuery.support.cors = true
default_options =
accepts:
json: 'application/json; version=2'
ajax = Em.Object.create
publicEndpoints: [/\/repos\/?.*/, /\/builds\/?.*/, /\/jobs\/?.*/]
privateEndpoints: [/\/repos\/\d+\/caches/]
get: (url, callback, errorCallback) ->
@ajax(url, 'get', success: callback, error: errorCallback)
post: (url, data, callback) ->
@ajax(url, 'post', data: data, success: callback)
patch: (url, data, callback) ->
@ajax(url, 'patch', data: data, success: callback)
needsAuth: (method, url) ->
return true if config.pro
return true if method != 'GET'
publicEndpoint = @publicEndpoints.find (pattern) ->
url.match(pattern)
privateEndpoint = @privateEndpoints.find (pattern) ->
url.match(pattern)
!publicEndpoint || privateEndpoint
ajax: (url, method, options) ->
# TODO: we have our own ajax implementation, because jQuery didn't
# properly worked with headers on firefox, it would be nice to check
# if this is still a problem and if we can remove this
method = method || "GET"
method = method.toUpperCase()
endpoint = config.apiEndpoint || ''
options = options || {}
token = Travis.sessionStorage.getItem('travis.token')
if token && (ajax.needsAuth(method, url) || options.forceAuth)
options.headers ||= {}
options.headers['Authorization'] ||= "token #{token}"
options.url = url = "#{endpoint}#{url}"
options.type = method
options.dataType = options.dataType || 'json'
options.context = this
if options.data && method != 'GET'
options.data = JSON.stringify(options.data)
if method != 'GET' && method != 'HEAD'
options.contentType = options.contentType || 'application/json; charset=utf-8'
success = options.success || (->)
options.success = (data, status, xhr) =>
Travis.lookup('controller:flash').loadFlashes(data.flash) if data?.flash
delete data.flash if data?
success.apply(this, arguments)
error = options.error || (->)
options.error = (data, status, xhr) =>
Travis.lookup('controller:flash').pushObject(data.flash) if data?.flash
delete data.flash if data?
error.apply(this, arguments)
options = $.extend(options, default_options)
if testMode?
console.log('Running ajax with', options.url)
# we use jquery.mockjax for test, I don't want to hack it or rewrite it,
# so let's just pretend we still use ajax if testing mode is on
return new Ember.RSVP.Promise( (resolve, reject) ->
oldSuccess = options.success
options.success = (json, status, xhr) ->
Ember.run this, ->
oldSuccess.call(this, json, status, xhr)
Ember.run(null, resolve, json)
oldError = options.error
options.error = (jqXHR) ->
if jqXHR
# for a context, please see https://github.com/emberjs/ember.js/issues/3051
jqXHR.then = null
Ember.run this, ->
oldError.call this, jqXHR
reject(jqXHR)
$.ajax(options)
)
if options.data && (method == "GET" || method == "HEAD")
params = jQuery.param(options.data)
delimeter = if url.indexOf('?') == -1 then '?' else '&'
url = url + delimeter + params
xhr = new XMLHttpRequest()
xhr.open(method, url)
if options.accepts && !options.headers?.accept?
accepts = []
for key, value of options.accepts
accepts.pushObject(value)
xhr.setRequestHeader('Accept', accepts.join(', '))
if options.headers
for name, value of options.headers
xhr.setRequestHeader(name, value)
if options.contentType
xhr.setRequestHeader('Content-Type', options.contentType)
resolve = null
reject = null
promise = new Ember.RSVP.Promise( (_resolve, _reject) ->
resolve = _resolve
reject = _reject
)
xhr.onreadystatechange = ->
if xhr.readyState == 4
contentType = xhr.getResponseHeader('Content-Type')
data = if contentType && contentType.match /application\/json/
try
jQuery.parseJSON(xhr.responseText)
catch e
console.log('error while parsing a response', method, options.url, xhr.responseText)
else
xhr.responseText
if xhr.status >= 200 && xhr.status < 300
resolve(data)
options.success.call(options.context, data, xhr.status, xhr)
else
reject(xhr)
options.error.call(data, xhr.status, xhr)
data = options.data
if typeof(options.data) == "object" && (Ember.isNone(options.contentType) || options.contentType.match /application\/json/)
data = JSON.stringify(data)
xhr.send(data)
return promise
`export default ajax`

View File

@ -0,0 +1,17 @@
`import Ember from 'ember'`
Storage = Ember.Object.extend
init: ->
@set('storage', {})
key: (key) ->
"__#{key.replace('.', '__')}"
getItem: (k) ->
return @get("storage.#{@key(k)}")
setItem: (k,v) ->
@set("storage.#{@key(k)}", v)
removeItem: (k) ->
@setItem(k, null)
clear: ->
@set('storage', {})
`export default Storage`

View File

@ -26,6 +26,7 @@ languageConfigKeys = {
lein: 'Lein' lein: 'Lein'
compiler: 'Compiler' compiler: 'Compiler'
crystal: 'Crystal' crystal: 'Crystal'
osx_image: 'Xcode'
} }
configKeys = { configKeys = {

View File

@ -1,13 +1,13 @@
`import ENV from 'travis/config/environment'` `import ENV from 'travis/config/environment'`
`import Ajax from 'travis/utils/ajax'`
TravisPusher = (config) -> TravisPusher = (config, ajaxService) ->
@init(config) @init(config, ajaxService)
this this
TravisPusher.prototype.active_channels = [] TravisPusher.prototype.active_channels = []
TravisPusher.prototype.init = (config) -> TravisPusher.prototype.init = (config, ajaxService) ->
this.ajaxService = ajaxService
Pusher.warn = @warn.bind(this) Pusher.warn = @warn.bind(this)
Pusher.host = config.host if config.host Pusher.host = config.host if config.host
@pusher = new Pusher(config.key, encrypted: config.encrypted, disableStats: true) @pusher = new Pusher(config.key, encrypted: config.encrypted, disableStats: true)
@ -93,7 +93,7 @@ if ENV.pro
unless channels.fetching unless channels.fetching
channels.fetching = true channels.fetching = true
Ajax.post Pusher.channel_auth_endpoint, { socket_id: socketId, channels: names }, (data) -> TravisPusher.ajaxService.post Pusher.channel_auth_endpoint, { socket_id: socketId, channels: names }, (data) ->
channels.fetching = false channels.fetching = false
callback(data.channels) for callback in channels.callbacks callback(data.channels) for callback in channels.callbacks

View File

@ -1,24 +0,0 @@
`import Ember from 'ember'`
Slider = (storage) ->
@minimize() if storage.getItem('travis.maximized') == 'true'
this
Slider.prototype.persist = ->
Travis.storage.setItem('travis.maximized', @isMinimized())
Slider.prototype.isMinimized = ->
return $('body').hasClass('maximized');
Slider.prototype.minimize = ->
$('body').addClass('maximized')
Slider.prototype.toggle = ->
$('body').toggleClass('maximized')
@persist()
# TODO gotta force redraws here :/
element = $('<span></span>')
$('#top .profile').append(element)
Ember.run.later (-> element.remove()), 10
`export default Slider`

View File

@ -14,7 +14,12 @@ Auth = Ember.Object.extend
signInForTests: (user) -> signInForTests: (user) ->
@set('state', 'signed-in') @set('state', 'signed-in')
if user.constructor.typeKey? != 'user' if user.constructor.typeKey? != 'user'
@store.pushPayload(users: [user]) @store.push(
data:
type: 'user',
id: user.id
attributes: user
)
user = @store.recordForId('user', user.id) user = @store.recordForId('user', user.id)
@set('currentUser', user) @set('currentUser', user)

Some files were not shown because too many files have changed in this diff Show More