Pemrograman yang mudah: Papan Kanban untuk GitLab dalam satu hari kerja

Senin kerja dimulai dengan dialog berikut:

Pemimpin (P): Tidak jelas dalam tim Anda siapa yang melakukan apa.
I (I): Ya, kami tidak memiliki alat yang akan mencerminkan gambaran umum pekerjaan pada tugas. Ada papan kanban di hitlab, tetapi mereka hanya dalam konteks proyek dan grup. Papan kanban biasa akan menyelesaikan masalah.
R: Lalu buat papan.
Saya: Pagi itu akan siap.

Cepat atau lambat, momen itu datang dalam kehidupan seorang pemimpin tim pemula ketika dia menyadari bahwa timnya membutuhkan papan kanban. Ini menghilangkan kecemasan tentang kontrol proses pengembangan dan memberikan kepercayaan di masa depan. Biasanya masalah ini diselesaikan dengan permintaan terendah kepada manajer untuk membeli papan magnetik, satu set stiker multi-warna dan beberapa spidol untuk papan tulis. Baik, atau menggunakan layanan seperti Jira. Tetapi tim kami memiliki satu pengembang di remote, dan gitlab di sirkuit tertutup (tidak tersedia dari Internet karena alasan keamanan informasi), jadi saya harus memilih yang paling sederhana dari dua solusi yang mungkin:

a) pembuatan lengan mekanis dan pengontrol untuknya, yang akan menempel ulang stiker pada jarak jauh, sementara agar tidak menyulitkan keputusan, kita harus menulis pada stiker untuk kolega kita yang tidak dapat diakses, yang tidak adil;
b) implementasi dewan kanban perangkat lunak yang akan mengumpulkan semua tugas dari hitlab kami.

Tentu saja, jiwa berbaring di tabung papan fisik. Saya bahkan mulai berpikir tentang menggunakan DualShock 4 sebagai pengontrol lengan mekanis, tetapi saya sendiri yang menetapkan batas waktu keesokan paginya, jadi saya harus bertahan dengan solusi perangkat lunak tanpa jiwa.


Sebelum mulai menggambarkan bagian teknis dari cerita ini, saya akan berbicara tentang bagaimana kami menggunakan gitlab di Ontla. Grup-grup di hitlab yang kami miliki berhubungan dengan pelanggan internal / eksternal atau proyek besar yang terpisah, dan proyek tersebut adalah tempat penyimpanan untuk kode layanan tertentu. Sebagai contoh, kami memiliki grup panel kontrol penagihan di mana empat proyek adalah layanan dan aplikasi web, dan ada grup pemasaran di mana ada proyek untuk situs web perusahaan, layanan mikro untuk integrasi dengan amoCRM, semua jenis halaman arahan dan sebagainya. Saat ini kami memiliki sembilan grup aktif, dan ada lebih sedikit programmer, sehingga semua programmer berpartisipasi dalam semua grup.

Dan yang kami butuhkan adalah halaman dengan semua tugas dari hitlab, dari semua grup dan proyek, tetapi fungsi ini di GitLab, sayangnya, tidak. Google cepat tidak membantu menemukan solusi mandiri

Kami akan membutuhkan:


- backend pada Node.js - untuk mengumpulkan tugas dengan GitLab dan mentransfernya ke klien;
- klien di Vue.js - untuk melakukannya dengan cepat dan indah;
- bumbui semuanya dengan TypeScript, tentu saja!
- PostgreSQL - di mana kami akan menyimpan informasi tentang tugas dan posisinya di papan tulis;
- 8 jam waktu kerja;
- Suasana hati yang baik.

Kondisi yang paling penting adalah pengembangan harus mudah dan menyenangkan, sebut saja "pemrograman mudah".

Kebun binatang


Untuk alasan yang jelas, saya tidak dapat menggunakan proyek dan tugas dari hitlab perusahaan kami untuk mengilustrasikan artikel tentang Habré, jadi saya menggunakan hitlab saya, dengan monyet dan buaya. Ya, ini adalah gitlab untuk kebun binatang fiksi, tetapi akan ada tugas yang tidak kalah serius dari pada gitlab lainnya. Lihatlah daftar proyek:


karkas


Selama bertahun-tahun pembangunan, saya telah mengembangkan visi saya sendiri tentang bagaimana aplikasi backend pada Node.js harus dibangun, dan pendekatan ini mengambil bentuk kerangka kerja - seperangkat file. Untuk membuat proyek baru, saya hanya menyalin direktori dengan templat. Ini tidak bisa berlangsung selamanya, dan saya membuat paket karcass npm (nama bangkai sudah diambil oleh penggemar sepeda yang sama seperti yang saya lakukan), yang mengotomatiskan proses menciptakan dasar untuk aplikasi:


Sekarang kita perlu berurusan dengan penerimaan dan penyimpanan kelompok, proyek, tugas dan, tentu saja, pemain.

TypeORM


"Aku melihat beberapa entitas ..."
- dari diskusi di forum paranormal

Karena kita perlu mempresentasikan data dari gitlab dalam bentuk yang nyaman bagi kita, maka dalam proses antara mengumpulkan data ini dan mempresentasikannya, kita perlu menyimpannya. Baru-baru ini, saya menemukan perpustakaan yang luar biasa untuk bekerja dengan database, TypeORM, luar biasa untuk ekosistem JS. Itu masih hijau, tetapi memiliki setiap kesempatan untuk mengusir Sekuel dari tahta.

Dalam beberapa puluh menit, migrasi dan kelas untuk pengguna , grup , proyek , dan tugas muncul. Ini salah satunya:

import { Entity, PrimaryColumn, Column } from 'typeorm' @Entity({ name: 'group' }) export class Group { @PrimaryColumn('integer') public id!: number @Column('varchar') public name!: string @Column('varchar') public url!: string } 

Dan untuk membuat migrasi, saya kembali membuat asisten sepeda CreateMigrationCommand . Perintah ini sangat mudah digunakan:


Di kelas Issue, selain bidang yang menyimpan data dari Gitlab, kami menambahkan bidang yang menjadi dasar seluruh keributan dimulai, yaitu, menunjukkan posisi tugas pada papan kanban:

 export class Issue extends AbstractEntity { /* ... */ @Column('varchar', { name: 'kanban_status' }) public kanbanStatus?: 'new'|'planed'|'working'|'checking'|'done' @Column('int', { name: 'kanban_order' }) public kanbanOrder?: number } 

Kami butuh informasi


Ketika saya pertama kali mulai berkenalan dengan GitLab, bagi saya itu tampak seperti produk sederhana (baca “benci”), tetapi bekerja dengannya setiap hari, saya menyadari betapa salahnya saya. Jadi kali ini saya terkejut - ternyata GitLab memiliki API yang sangat kaya yang mencakup hampir semua fungsi yang tersedia melalui antarmuka pengguna. Tetapi untuk menyelesaikan masalah kami, Anda hanya perlu empat metode, yang namanya berbicara sendiri: / pengguna , / grup , / proyek , / proyek /: id / masalah . GitlabService akan bertanggung jawab atas interaksi langsung dengan GitLab, dan kelas-kelas lain akan mengaksesnya. Misalnya, penerapan metode updateGroups dari kelas GroupService terlihat seperti ini:

 export class GroupService extends AbstractService { /* ... */ public async updateGroups() { for (const data of await this.app.gitlabService.getGroups()) { // <=     GitlabService let group = await this.getGroup(data.id) if (!group) { group = this.groupRepository.create({ id: data.id, }) } group.name = data.name group.url = data.web_url await this.groupRepository.save(group) } } /* ... */ } 

Perintah updateProjectsCommand akan bertanggung jawab untuk pengoperasian memperoleh informasi dari gitab dan memperbarui informasi yang relevan di basis data kami, yang dapat dipanggil dari konsol, tetapi aplikasi kami akan meluncurkannya sendiri dengan frekuensi yang ditentukan dalam konfigurasi:

 export class Application { /* ... */ protected initCron() { if (this.config.gitlab.updateInterval) { setInterval(async () => { if (!this.updateProjectsCommand) { this.updateProjectsCommand = new UpdateProjectsCommand(this) } await this.updateProjectsCommand.execute() }, this.config.gitlab.updateInterval * 1000) } } /* ... */ } 

Kami tidak akan membuat kode angka, warna, dan nama kolom di papan tulis, tetapi membuatnya dapat disesuaikan di config.js. Kami memiliki lima dari mereka, tetapi untuk seseorang, mungkin hanya dua yang diperlukan dan warna asam:

 columns: [ { key: 'new', title: '', color: 'rgb(255, 255, 219)' }, { key: 'planed', title: '', color: 'rgb(236, 236, 191)' }, { key: 'working', title: ' ', color: 'rgb(253, 214, 162)' }, { key: 'checking', title: ' ', color: 'rgb(162, 226, 253)' }, { key: 'done', title: '', color: 'rgb(162, 253, 200)' }, ], 

Yang utama adalah setidaknya harus ada dua, jika tidak aplikasi tidak akan mulai.

Untuk berinteraksi dengan bagian depan, kita membutuhkan metode yang menerima tugas dari database dan mengurutkannya menjadi "kolom":

Tampilkan IssueService.ts
 export class IssueService extends AbstractService { /* ... */ public async getKanban() { let issues = await this.issueRepository.find({ where: { updatedTimestamp: MoreThanOrEqual(new Date().getTimestamp() - 60 * 60 * 24 * 30) }, order: { kanbanOrder: 'ASC', updatedTimestamp: 'DESC' }, }) const keys = this.app.config.columns.map(c => c.key) const result: { [key: string]: Issue[] } = {} for (let ki = keys.length - 1; ki >= 0; ki--) { const key = keys[ki] if (ki === keys.length - 1) { //        result[key] = issues.filter(i => i.closed) } else if (ki === 0) { // ,  ,    ,    result[key] = issues } else { result[key] = issues.filter(i => i.kanbanStatus === key) } issues = issues.filter(i => !result[key].includes(i)) //     } return result } /* ... */ } 

Dan pengontrol yang menyimpan posisi tugas baru:

Tampilkan IssueController.ts
 export default class IssueController extends AbstractController { /* ... */ public async kanbanUpdate(data: IQueryData) { const keys = this.app.config.columns.map(c => c.key) for (const c of data.params as { key: string, title: string, color: string, issues: number[] }[]) { if (keys.indexOf(c.key) < 0) { continue } let index = 0 for (const i of c.issues) { index++ const issue = await this.app.issueService.getIssue(i) if (!issue) { continue } issue.kanbanStatus = c.key issue.kanbanOrder = index this.app.issueService.issueRepository.save(issue) } } } } 

Stosh. Kami mengumpulkan informasi dari GitLab, menyiapkan data untuk menampilkan papan, bahkan tahu cara menyimpan posisi tugas yang diperbarui di papan tulis. Satu-satunya yang tersisa adalah membuat papan itu sendiri. Dan di sini kita dipersenjatai dengan alat yang ampuh:

Vue CLI - Alih-alih Seribu Kata



TSX


Untuk memfasilitasi refactoring, menemukan bug, linting dan ketenangan pikiran lainnya, kami menggunakan templat tsx yang hampir semuanya didukung oleh Vue.js. Misalnya, komponen terpenting di papan kami, komponen presentasi tugas:

Tampilkan kode Issue.tsx
 import { Vue, Component, Prop, Watch } from 'vue-property-decorator' import './Issue.css' import { CreateElement, VNode } from 'vue' interface IIssue { groupId: number groupName: string groupUrl: string projectName: string projectUrl: string url: string title: string executor: { color: string, name: string } spent: number estimate: number } @Component export default class extends Vue { @Prop() public issue!: IIssue public issueValue!: IIssue public selectUser = false @Watch('issue', { immediate: true }) public onIssueChange() { this.issueValue = this.issue } // eslint-disable-next-line @typescript-eslint/no-unused-vars public render(h: CreateElement): VNode { return <div class="kvcIssue"> <div class="kvciTitle"> { this.issueValue.groupId ? <small><a href={ this.issueValue.groupUrl } target="_blank"> { this.issueValue.groupName } </a> / </small> : undefined } <a href={ this.issueValue.projectUrl } target="_blank">{ this.issueValue.projectName }</a> </div> <div class="kvciText"><a href={ this.issueValue.url } target="_blank">{ this.issueValue.title }</a></div> <div class={ ['kvciExecutor', this.issueValue.executor ? '' : 'none'] }> <span class="timeTd"> <span title=" ">{ this.issueValue.spent.time() }</span> / <span title=" "> { this.issueValue.estimate.time() }</span> </span> { this.issueValue.executor ? <span class="kvcieUser" style={ { background: this.issueValue.executor.color } }> { this.issueValue.executor.name } </span> : <span class="kvcieSelect"> </span> } </div> </div> } } 

Komponen ini menampilkan bilah tugas. Perhatikan bahwa tugas sebenarnya telah menghabiskan waktu dan merencanakan:


Ada juga komponen Column.tsx dan Kanban.tsx . Hanya tiga komponen yang menyediakan presentasi tugas di papan tulis:


Vue.Draggable , yang kakinya tumbuh dari SortableJS , bertanggung jawab untuk memindahkan tugas di papan tulis. Sangat mudah digunakan:

 import Draggable from 'vuedraggable' /* ... */ export default class extends Vue { /* ... */ public render(h: CreateElement): VNode { /* ... */ <Draggable class="kvcIssues" vModel={ this.issuesValue } group={ { name: 'issues', pull: true, put: true } } onEnd={ this.onDrag } onAdd={ this.onDrag } > { this.issuesValue.map(i => <Issue key={ i.id } issue={ i } />) } </Draggable> /* ... */ } } 

Tentunya Anda memperhatikan bahwa setiap pengguna memiliki warna sendiri, yang digunakan untuk menampilkan login. Ini sangat mudah - di antara banyak tugas, Anda dapat dengan cepat menemukan sendiri. Ada dua cara untuk mencapai "multicolority": untuk mengatur warna dengan tangan untuk setiap pengguna atau untuk menghasilkannya berdasarkan sesuatu.

Beri saya nama pengguna Anda dan saya akan memberi tahu Anda warna apa yang Anda miliki


Pembuat warna dari kelas Pengguna bertanggung jawab untuk pembuatan warna. Saya secara khusus mengimplementasikan fungsi ini di backend, karena saya membutuhkan kriptografi, dan merupakan kejahatan untuk menyeret seluruh perpustakaan ke depan untuk tugas sekecil itu. Dan itu lebih tepat ketika bagian belakang, dan bukan bagian depan, bertanggung jawab atas karakteristik entitas:

 export class User extends AbstractEntity { /* ... */ public get color() { const hash = crypto.createHash('md5').update(this.username).digest() //  md5       .     -   . const result: number[] = [] for (let i = 0; i < 3; i++) { result.push(Math.round(200 - hash[i] / 255 * 150)) //      ,     ,       } return `rgb(${result.join(', ')})` } } 

Bagi mereka yang ingin tahu warna apa yang ditakdirkan untuk mereka, saya tidak terlalu malas untuk memotong cuplikan pada CodePen .

Itu semua


Bekerja pada hari Selasa dimulai dengan dialog berikut:

R: Baiklah, bagaimana papannya, siap? ..
Saya: Ya, di sini.
R: Oh, keren!

Hasil dari "hari kerja" saya ada di sini: https://github.com/onlanta/kanban . Anda tidak hanya dapat melihatnya, menyentuhnya, tetapi juga menggunakannya (instruksi di sana). Dan Anda dapat membangun pelacak waktu penuh untuk hitlab, yang kami lakukan di perusahaan.

Mengapa saya memutuskan untuk menulis catatan ini? Ini adalah salah satu momen cerah baru-baru ini dalam aktivitas profesional saya, ketika tugas itu penting, menarik, dan pada saat yang sama cukup sederhana, dan solusinya ternyata mudah dan elegan. Saya sarankan di komentar untuk mengingat tugas serupa dari latihan Anda.

Source: https://habr.com/ru/post/id462855/


All Articles