diff --git a/app/components/settings-modal.js b/app/components/settings-modal.js index 387173c..d7711bd 100644 --- a/app/components/settings-modal.js +++ b/app/components/settings-modal.js @@ -8,6 +8,7 @@ export default Component.extend({ // Properties token_github_com: oneWay('userSettings.tokens.github_com'), + token_gitlab_com: oneWay('userSettings.tokens.gitlab_com'), init(...args) { this._super(...args); @@ -19,6 +20,7 @@ export default Component.extend({ }, saveSettings() { this.userSettings.setToken('github_com', this.get('token_github_com')); + this.userSettings.setToken('gitlab_com', this.get('token_gitlab_com')); this.set('isActive', false); }, }, diff --git a/app/controllers/tasks/github.js b/app/controllers/tasks/github.js new file mode 100644 index 0000000..d630f31 --- /dev/null +++ b/app/controllers/tasks/github.js @@ -0,0 +1,4 @@ +import Controller from '@ember/controller'; + +export default Controller.extend({ +}); diff --git a/app/data/organizations.js b/app/data/organizations.js index 70633d9..eeaab5f 100644 --- a/app/data/organizations.js +++ b/app/data/organizations.js @@ -11,7 +11,11 @@ export default { }, { type: 'gitlab', - identifier: 'coala', + identifier: 'coala/mobans', + }, + { + type: 'gitlab', + identifier: 'coala/coala-utils', }, ], }, diff --git a/app/models/gitlab-task.js b/app/models/gitlab-task.js new file mode 100644 index 0000000..bef98f1 --- /dev/null +++ b/app/models/gitlab-task.js @@ -0,0 +1,3 @@ +import TaskModel from './task'; + +export default TaskModel.extend({}); diff --git a/app/router.js b/app/router.js index 88ca55f..e0066b4 100644 --- a/app/router.js +++ b/app/router.js @@ -9,6 +9,7 @@ const Router = EmberRouter.extend({ Router.map(function mainRoute() { return this.route('tasks', function tasksRoute() { this.route('github'); + this.route('gitlab'); }); }); diff --git a/app/routes/tasks/gitlab.js b/app/routes/tasks/gitlab.js new file mode 100644 index 0000000..2b38260 --- /dev/null +++ b/app/routes/tasks/gitlab.js @@ -0,0 +1,23 @@ +import Route from '@ember/routing/route'; +import { inject } from '@ember/service'; + +export default Route.extend({ + gitlab: inject(), + store: inject(), + organizations: inject(), + + model(params, transition) { + const { org } = transition.queryParams; + const store = this.get('store'); + const projects = this.get('organizations').fetchGitlabProjects(org); + if (projects.length > 0) { + return this.get('gitlab').tasks({ projects }).then((data) => { + data.forEach((task) => { + store.pushPayload('gitlab-task', task); + }); + return store.peekAll('gitlab-task'); + }); + } + return []; + }, +}); diff --git a/app/serializers/gitlab-task.js b/app/serializers/gitlab-task.js new file mode 100644 index 0000000..b913727 --- /dev/null +++ b/app/serializers/gitlab-task.js @@ -0,0 +1,30 @@ +import DS from 'ember-data'; + +export default DS.JSONSerializer.extend({ + pushPayload(store, payload) { + const task = {}; + task.id = payload.id; + task.bodyText = payload.description; + task.commentCount = payload.user_notes_count; + // Set the default color to 65C8FF, otherwise, we need to refetch the default + // color from `project/:id/labels` endpoint. + task.labels = payload.labels.map(label => ({ color: '65C8FF', name: label })); + task.updatedAt = payload.updated_at; + task.title = payload.title; + task.url = payload.web_url; + + task.author = {}; + task.author.url = payload.author.web_url; + task.author.login = payload.author.username; + task.author.avatarUrl = payload.author.avatar_url; + + task.repository = {}; + task.repository.nameWithOwner = payload.repository.path_with_namespace; + task.repository.url = payload.repository.web_url; + + task.isPullRequest = payload._type === 'PullRequest'; + + store.push(this.normalize(store.modelFor('gitlab-task'), task)); + return task; + }, +}); diff --git a/app/services/gitlab.js b/app/services/gitlab.js new file mode 100644 index 0000000..3fa1f62 --- /dev/null +++ b/app/services/gitlab.js @@ -0,0 +1,53 @@ +import { inject } from '@ember/service'; +import AjaxService from 'ember-ajax/services/ajax'; +import RSVP from 'rsvp'; + +const ENDPOINT = 'https://gitlab.com/api/v4'; +const PROJECT_ENDPOINT = `${ENDPOINT}/projects/{projectId}`; +const ISSUE_ENDPOINT = `${PROJECT_ENDPOINT}/issues?order_by=created_at&state=opened&per_page=100`; + +function buildIssuesUrl(projectId) { + return ISSUE_ENDPOINT.replace('{projectId}', encodeURIComponent(projectId)); +} + +function buildProjectUrl(projectId) { + return PROJECT_ENDPOINT.replace('{projectId}', encodeURIComponent(projectId)); +} + +export default AjaxService.extend({ + userSettings: inject(), + + host: 'https://gitlab.com/', + _issueUrl: '', + + init(...arg) { + this._super(...arg); + this.set('headers', { 'Private-Token': this.get('userSettings').tokens.get('gitlab_com') }); + }, + + tasks({ projects }) { + const tasks = projects.map((projectId) => { + const embedRepoInfo = taskList => this.fetchRepositoryInfo(projectId) + .then(repoInfo => taskList.map((task) => { + const decoratedTask = Object.assign({}, task); + decoratedTask.repository = repoInfo; + decoratedTask.type = 'gitlab-task'; + return decoratedTask; + })); + return this.fetchIssues(projectId).then(embedRepoInfo); + }); + + return RSVP.all(tasks).then(taskList => + taskList + .reduce((combinedTasks, initialTasklist) => combinedTasks.concat(initialTasklist), [])); + }, + + fetchRepositoryInfo(projectId) { + return this.request(buildProjectUrl(projectId)); + }, + + fetchIssues(projectId) { + return this.request(buildIssuesUrl(projectId)); + }, + +}); diff --git a/app/services/organizations.js b/app/services/organizations.js index c04e628..550b16f 100644 --- a/app/services/organizations.js +++ b/app/services/organizations.js @@ -1,11 +1,11 @@ -import Object, { computed } from '@ember/object'; +import EmberObject, { computed } from '@ember/object'; import Service from '@ember/service'; import organizations from '../data/organizations'; export default Service.extend({ init(...args) { this._super(...args); - this.organizations = Object.create(organizations); + this.organizations = EmberObject.create(organizations); }, list: computed('organizations', function getOrganizationList() { @@ -15,4 +15,18 @@ export default Service.extend({ fetch(slug) { return this.organizations.get(slug); }, + + fetchGitlabProjects(slug) { + const { trackers } = this.fetch(slug); + if (!trackers) { + return []; + } + return trackers.reduce((previous, tracker) => { + if (tracker.type === 'gitlab') { + return [...previous, tracker.identifier]; + } + return previous; + }, []); + }, + }); diff --git a/app/templates/components/settings-modal.hbs b/app/templates/components/settings-modal.hbs index 39f55d8..7af925c 100644 --- a/app/templates/components/settings-modal.hbs +++ b/app/templates/components/settings-modal.hbs @@ -14,6 +14,14 @@
In order to fetch results from GitHub, you must specify GitHub token. With ANY scope you like. No exact scope is required.
Get GitHub user token from https://github.com/settings/tokens
+In order to fetch results from GitLab, you must specify GitLab token. With ANY scope you like. No exact scope is required.
+Get GitHub user token from https://github.com/settings/tokens
+