Skip to content

Commit

Permalink
feat(access-tokens): access token form & repository settings
Browse files Browse the repository at this point in the history
  • Loading branch information
jkuri committed Aug 20, 2017
1 parent 4ee4f6e commit fead919
Show file tree
Hide file tree
Showing 18 changed files with 253 additions and 76 deletions.
27 changes: 27 additions & 0 deletions src/api/db/access-token.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { AccessToken } from './model';

export function getAccessTokens(): Promise<any> {
return new Promise((resolve, reject) => {
new AccessToken().fetchAll({ withRelated: ['user'] })
.then(tokens => {
if (!tokens) {
reject(tokens);
}

const result = tokens.toJSON().map(token => {
delete token.token;
delete token.user.password;
return token;
});

resolve(result);
});
});
}

export function insertAccessToken(data: any): Promise<any> {
return new Promise((resolve, reject) => {
new AccessToken().save(data, { method: 'insert' })
.then(token => !token ? reject(token) : resolve(token.toJSON()));
});
}
5 changes: 2 additions & 3 deletions src/api/db/migrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,8 @@ export function create(): Promise<null> {
t.string('user_avatar_url');
t.string('user_url');
t.string('user_html_url');
t.string('username');
t.string('password');
t.string('access_token');
t.integer('access_tokens_id');
t.foreign('access_tokens_id').references('access_tokens.id');
t.json('data');
t.timestamps();
}))
Expand Down
8 changes: 8 additions & 0 deletions src/api/db/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,20 @@ import { Bookshelf } from './config';
export class User extends Bookshelf.Model<any> {
get tableName() { return 'users'; }
get hasTimestamps() { return true; }
access_tokens() { return this.hasMany(AccessToken, 'users_id'); }
}

export class AccessToken extends Bookshelf.Model<any> {
get tableName() { return 'access_tokens'; }
get hasTimestamps() { return true; }
user() { return this.belongsTo(User, 'users_id'); }
}

export class Repository extends Bookshelf.Model<any> {
get tableName() { return 'repositories'; }
get hasTimestamps() { return true; }
builds() { return this.hasMany(Build, 'repositories_id'); }
access_token() { return this.belongsTo(AccessToken, 'access_tokens_id'); }
}

export class Build extends Bookshelf.Model<any> {
Expand Down
41 changes: 34 additions & 7 deletions src/api/db/repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,38 @@ export function getRepository(id: number): Promise<any> {
}
},
'builds.repository',
'builds.jobs'
'builds.jobs.runs',
'access_token.user'
]
} as any)
.then(repo => {
if (!repo) {
reject(repo);
} else {
resolve(repo.toJSON());
repo = repo.toJSON();
repo.builds = repo.builds.map(build => {
build.jobs = build.jobs.map(job => {
if (job.runs.length > 0) {
job.end_time = job.runs[job.runs.length - 1].end_time;
job.start_time = job.runs[job.runs.length - 1].start_time;
job.status = job.runs[job.runs.length - 1].status;
}

return job;
});

return build;
});

resolve(repo);
}
}).catch(err => reject(err));
});
}

export function getRepositoryOnly(id: number): Promise<any> {
return new Promise((resolve, reject) => {
new Repository({ id: id }).fetch().then(repo => {
new Repository({ id: id }).fetch({ withRelated: ['access_token.user'] }).then(repo => {
if (!repo) {
reject(repo);
} else {
Expand All @@ -51,9 +67,13 @@ export function getRepositoryBadge(id: number): Promise<string> {
resolve('unknown');
} else {
repo = repo.toJSON();
let status = 'queued';
let status = 'unknown';

if (repo.builds[0] && repo.builds[0].runs[0]) {
if (repo.builds[0].runs[0].job_runs.findIndex(run => run.status === 'queued') !== -1) {
status = 'queued';
}

if (repo.builds[0] && repo.builds[0].runs[0]) {
if (repo.builds[0].runs[0].job_runs.findIndex(run => run.status === 'failed') !== -1) {
status = 'failing';
}
Expand Down Expand Up @@ -107,8 +127,8 @@ export function getRepositories(userId: string, keyword: string): Promise<any[]>

export function getRepositoryId(owner: string, repository: string): Promise<any> {
return new Promise((resolve, reject) => {
new Repository().query(q => q.where('full_name', `${owner}/${repository}`))
.fetch().then(repo => !repo ? reject() : resolve(repo.toJSON().id));
new Repository().query(q => q.where('full_name', `${owner}/${repository}`)).fetch()
.then(repo => !repo ? reject() : resolve(repo.toJSON().id));
});
}

Expand All @@ -124,6 +144,13 @@ export function addRepository(data: any): Promise<any> {
});
}

export function saveRepositorySettings(data: any): Promise<any> {
return new Promise((resolve, reject) => {
new Repository({ id: data.id }).save(data, { method: 'update', require: false })
.then(repo => !repo ? reject(repo) : resolve(repo.toJSON()));
});
}

export function updateRepository(data: any): Promise<boolean> {
return new Promise((resolve, reject) => {
new Repository().where({ github_id: data.github_id })
Expand Down
21 changes: 14 additions & 7 deletions src/api/db/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,20 @@ import { generatePassword, comparePassword, generateJwt } from '../security';

export function getUser(id: number): Promise<any> {
return new Promise((resolve, reject) => {
new User({ id: id }).fetch().then(user => {
if (!user) {
reject(user);
} else {
resolve(user.toJSON());
}
});
new User({ id: id }).fetch({ withRelated: ['access_tokens'] })
.then(user => {
if (!user) {
reject(user);
} else {
let result = user.toJSON();
result.access_tokens = result.access_tokens.map(token => {
delete token.token;
return token;
});

resolve(result);
}
});
});
}

Expand Down
40 changes: 33 additions & 7 deletions src/api/server-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ import {
getRepositories,
getRepository,
getRepositoryBadge,
getRepositoryId
getRepositoryId,
saveRepositorySettings
} from './db/repository';
import { getBuilds, getBuild } from './db/build';
import { getJob } from './db/job';
import { insertAccessToken, getAccessTokens } from './db/access-token';
import { imageExists } from './docker';

export function webRoutes(): express.Router {
Expand Down Expand Up @@ -120,6 +122,24 @@ export function userRoutes(): express.Router {
.catch(err => res.status(400).json({ err: err }));
});

router.post('/add-token', (req: express.Request, res: express.Response) => {
insertAccessToken(req.body)
.then(() => res.status(200).json({ data: true }))
.catch(() => res.status(200).json({ data: false }));
});

return router;
}

export function tokenRoutes(): express.Router {
const router = express.Router();

router.get('/', (req: express.Request, res: express.Response) => {
getAccessTokens()
.then(tokens => res.status(200).json({ data: tokens }))
.catch(() => res.status(200).json({ data: false }));
});

return router;
}

Expand All @@ -144,6 +164,12 @@ export function repositoryRoutes(): express.Router {
}).catch(err => res.status(200).json({ status: false }));
});

router.post('/save', (req: express.Request, res: express.Response) => {
saveRepositorySettings(req.body)
.then(() => res.status(200).json({ data: true }))
.catch(() => res.status(200).json({ data: false }));
});

return router;
}

Expand All @@ -160,12 +186,12 @@ export function badgeRoutes(): express.Router {

router.get('/:owner/:repository', (req: express.Request, res: express.Response) => {
getRepositoryId(req.params.owner, req.params.repository)
.then(id => getRepositoryBadge(id))
.then(status => {
res.writeHead(200, {'Content-Type': 'image/svg+xml'});
res.write(generateBadgeHtml(status));
res.end();
});
.then(id => getRepositoryBadge(id))
.then(status => {
res.writeHead(200, {'Content-Type': 'image/svg+xml'});
res.write(generateBadgeHtml(status));
res.end();
});
});

return router;
Expand Down
1 change: 1 addition & 0 deletions src/api/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export class ExpressServer implements IExpressServer {
app.use('/webhooks', webhooks);
app.use('/api/setup', routes.setupRoutes());
app.use('/api/user', routes.userRoutes());
app.use('/api/tokens', routes.tokenRoutes());
app.use('/api/repositories', routes.repositoryRoutes());
app.use('/api/builds', routes.buildRoutes());
app.use('/api/jobs', routes.jobRoutes());
Expand Down
8 changes: 5 additions & 3 deletions src/api/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,20 +131,22 @@ export function generateBadgeHtml(status: string): string {
background = '#ffd43b';
} else if (status === 'queued') {
background = '#ffd43b';
} else if (status === 'unknown') {
background = '#3A7EE1';
} else {
background = '#39B54A';
}

return `
<svg xmlns="http://www.w3.org/2000/svg" width="97" height="20" style="shape-rendering:
<svg xmlns="http://www.w3.org/2000/svg" width="110" height="20" style="shape-rendering:
geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd;
clip-rule:evenodd">
<linearGradient id="b" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<mask id="a">
<rect width="97" height="20" rx="3" fill="#fff"/>
<rect width="110" height="20" rx="3" fill="#fff"/>
</mask>
<g mask="url(#a)">
<path fill="#333" d="M0 0h53v20H0z"/>
Expand All @@ -171,7 +173,7 @@ c-0.02-0.32,0.12-0.66-0.02-0.98c-0.75-0.04-1.5,
c0.46-0.82,1.25-1.45,2.16-1.67c0.7-0.06,1.41-0.01,2.1-0.02c-0.01-1.6,
0-3.2-0.01-4.81C18.09,13.33,18.42,12.27,19.18,11.57z"/>
</g>
<g fill="#fff" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="9">
<g fill="#fff" font-family="Verdana,Geneva,sans-serif" font-size="9">
<text x="22" y="15" fill="#010101" fill-opacity=".3">build</text>
<text x="22" y="14">build</text>
<text x="58" y="15" fill="#010101" fill-opacity=".3">` + status + `</text>
Expand Down
23 changes: 16 additions & 7 deletions src/app/components/app-repository/app-repository.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,23 @@ <h1>{{ repo?.full_name }}</h1>
</div>

<div class="column is-12" *ngIf="tab === 'settings'">
<div class="columns is-multiline">
<div class="columns">
<div class="column is-12">
<h2>Settings</h2>
</div>
<div class="column is-12">
<form class="form" #repoForm="ngForm" (ngSubmit)="saveRepoSettings($event)">

</form>
<div class="settings-container">
<h2>Settings</h2>
<form class="form" #repoForm="ngForm" (ngSubmit)="saveRepoSettings($event)">
<div class="form-field">
<label class="form-label">Access Token</label>
<select class="form-input form-select" [(ngModel)]="form.access_tokens_id" name="access_tokens_id">
<option *ngFor="let t of tokens" [value]="t.id">{{ t.user.fullname }}`s {{ t.description }}</option>
</select>
</div>
<input type="hidden" name="id" [(ngModel)]="form.id">
<div class="form-field">
<button type="submit" class="button green float-right" [class.is-loading]="saving" [disabled]="!repoForm.form.valid">Save Repository Settings</button>
</div>
</form>
</div>
</div>
</div>
</div>
Expand Down
31 changes: 30 additions & 1 deletion src/app/components/app-repository/app-repository.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ import { ApiService } from '../../services/api.service';
import { ConfigService } from '../../services/config.service';
import { Subscription } from 'rxjs/Subscription';
import { format, distanceInWordsToNow } from 'date-fns';
import 'rxjs/add/operator/delay';

export interface IRepoForm {
id: number;
access_tokens_id: any;
}

@Component({
selector: 'app-repository',
Expand All @@ -18,6 +24,9 @@ export class AppRepositoryComponent implements OnInit, OnDestroy {
repo: any;
url: string;
statusBadge: string;
tokens: any[];
saving: boolean;
form: IRepoForm;

constructor(
private route: ActivatedRoute,
Expand All @@ -30,7 +39,7 @@ export class AppRepositoryComponent implements OnInit, OnDestroy {
}

ngOnInit() {
this.tab = 'settings';
this.tab = 'builds';
this.url = this.config.url;

this.route.params.subscribe(params => {
Expand All @@ -40,6 +49,7 @@ export class AppRepositoryComponent implements OnInit, OnDestroy {
} else {
this.fetch();
this.fetchBadge();
this.fetchTokens();
}
});

Expand Down Expand Up @@ -94,6 +104,7 @@ export class AppRepositoryComponent implements OnInit, OnDestroy {
fetch(): void {
this.api.getRepository(this.id).subscribe(event => {
this.repo = event;
this.form = { id: parseInt(this.id, 10), access_tokens_id: event.access_tokens_id };
this.loading = false;
this.updateJobs();
setInterval(() => this.updateJobs(), 1000);
Expand All @@ -108,6 +119,12 @@ export class AppRepositoryComponent implements OnInit, OnDestroy {
});
}

fetchTokens(): void {
this.api.getAllTokens().subscribe(tokens => {
this.tokens = tokens;
});
}

updateJobs(): void {
let currentTime = new Date().getTime() - this.socketService.timeSyncDiff;

Expand Down Expand Up @@ -155,4 +172,16 @@ export class AppRepositoryComponent implements OnInit, OnDestroy {
gotoBuild(buildId: number) {
this.router.navigate(['build', buildId]);
}

saveRepoSettings(e: MouseEvent): void {
this.saving = true;

this.api.saveRepositorySettings(this.form)
.delay(1000)
.subscribe(saved => {
if (saved) {
this.saving = false;
}
});
}
}
Loading

0 comments on commit fead919

Please sign in to comment.