Der Arbeitsmontag begann mit folgendem Dialog:
Anführer (P): In Ihrem Team ist nicht klar, wer was tut.
I (I): Ja, wir haben kein Tool, das das allgemeine Bild der Arbeit an Aufgaben widerspiegelt. Es gibt Kanban-Boards im Hitlab, aber sie sind nur im Kontext von Projekten und Gruppen. Ein gemeinsames Kanban-Board würde das Problem lösen.
R: Dann mach ein Brett.
Ich: Am Morgen wird es fertig sein.
Früher
oder später kommt der Moment im Leben eines Anfängerteamleiters, als er erkennt, dass sein Team ein Kanban-Board benötigt. Es beseitigt die Angst vor der Kontrolle des Entwicklungsprozesses und gibt Vertrauen in die Zukunft. Normalerweise wird dieses Problem durch die niedrigste Aufforderung an den Manager gelöst, eine Magnettafel, einen Satz mehrfarbiger Aufkleber und einige Markierungen für die Platine zu kaufen. Nun, oder mit Diensten wie Jira. Unser Team hat jedoch einen Entwickler auf der Fernbedienung und Gitlab in einem geschlossenen Kreislauf (aus Gründen der Informationssicherheit nicht im Internet verfügbar). Daher musste ich die einfachste von zwei möglichen Lösungen auswählen:
a) die Schaffung eines mechanischen Arms und eines Controllers dafür, der die Aufkleber aus der Ferne wieder auf die Tafel klebt. Um die Entscheidung nicht zu erschweren, müssen wir die Aufkleber für unseren unzugänglichen Kollegen aufschreiben, was unfair ist.
b) Implementierung eines Software-Kanban-Boards, das alle Aufgaben von unserem Hitlab sammelt.
Natürlich lag die Seele an der Röhre des physischen Brettes. Ich habe sogar darüber nachgedacht, DualShock 4 als mechanische Armsteuerung zu verwenden, aber ich selbst habe die Frist am nächsten Morgen festgelegt, sodass ich mit einer seelenlosen Softwarelösung auskommen musste.

Bevor ich den technischen Teil der Geschichte beschreibe, erzähle ich Ihnen, wie wir Gitlab in Onlanta verwenden. Die Gruppen im Hitlab, die wir haben, entsprechen dem internen / externen Kunden oder einem separaten großen Projekt, und das Projekt ist ein Repository für den Code eines bestimmten Dienstes. Zum Beispiel haben wir eine Gruppe von Abrechnungskontrollfeldern, in denen vier Projekte Dienste und Webanwendungen sind, und es gibt eine Marketinggruppe, in der es ein Projekt für eine Unternehmenswebsite, einen Mikroservice für die Integration mit amoCRM, alle Arten von Zielseiten usw. gibt. Wir haben derzeit neun aktive Gruppen und es gibt weniger Programmierer, sodass alle Programmierer an allen Gruppen teilnehmen.
Und alles, was wir brauchten, war eine Seite mit allen Aufgaben aus dem Hitlab, aus allen Gruppen und Projekten, aber diese Funktionalität in GitLab ist leider nicht. Quick Google hat daher nicht geholfen, eine eigenständige Lösung zu finden
Wir werden brauchen:
- Backend auf Node.js - zum Sammeln von Aufgaben mit GitLab und zum Übertragen auf den Client;
- Client auf Vue.js - um es schnell und schön zu machen;
- natürlich alles mit TypeScript würzen!
- PostgreSQL - wo wir Informationen über Aufgaben und ihre Position an der Tafel speichern;
- 8 Stunden Arbeitszeit;
- gute Laune.
Die wichtigste Bedingung ist, dass die Entwicklung einfach und unterhaltsam sein sollte. Nennen wir es "einfache Programmierung".
Der Zoo
Aus offensichtlichen Gründen kann ich die Projekte und Aufgaben aus unserem Unternehmens-Hitlab nicht verwenden, um den Artikel über Habré zu veranschaulichen. Deshalb habe ich mein Hitlab mit Affen und Krokodilen bereitgestellt. Ja, dies ist ein Gitlab für einen fiktiven Zoo, aber dort werden nicht weniger ernsthafte Aufgaben gestellt als in jedem anderen Gitlab. Schauen Sie sich die Liste der Projekte an:
Karcass
Im Laufe der Jahre der Entwicklung habe ich meine eigene Vision entwickelt, wie die Backend-Anwendung auf Node.js erstellt werden soll, und dieser Ansatz hatte die Form eines Frameworks - einer Reihe von Dateien. Um ein neues Projekt zu erstellen, habe ich einfach das Verzeichnis mit der Vorlage kopiert. Dies konnte nicht ewig so weitergehen, und ich erstellte das
Karcass-Npm- Paket (der Name
Carcass wurde bereits von demselben Fahrrad-Enthusiasten wie ich übernommen), das den Prozess der Erstellung der Basis für die Anwendung automatisiert:
Jetzt müssen wir uns mit dem Empfang und der Speicherung von Gruppen, Projekten, Aufgaben und natürlich Darstellern befassen.
TypeORM
"Ich sehe einige Entitäten ..."
- aus Diskussionen im Hellseher-Forum
Da wir die Daten aus dem Gitlab in einer für uns geeigneten Form präsentieren müssen, müssen wir sie zwischen dem Sammeln und Präsentieren dieser Daten irgendwie speichern. Kürzlich entdeckte ich die erstaunliche Bibliothek
TypeORM für die Arbeit mit Datenbanken, die für das JS-Ökosystem erstaunlich ist. Es ist immer noch grün, hat aber jede Chance, Sequelize vom Thron zu verdrängen.
In wenigen zehn Minuten werden Migrationen und Klassen für
Benutzer ,
Gruppen ,
Projekte und
Aufgaben angezeigt. Hier ist einer von ihnen:
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 }
Und um Migrationen zu erstellen, habe ich wieder ein
CreateMigrationCommand- Assistentenrad erstellt. Dieser Befehl ist sehr einfach zu verwenden:
In der Issue-Klasse fügen wir zusätzlich zu den Feldern, in denen Daten aus Gitlab gespeichert sind, Felder hinzu, aufgrund derer die ganze Aufregung begann, dh die Position der Aufgabe auf der Kanban-Tafel angibt:
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 }
Wir brauchen Informationen
Als ich anfing, GitLab kennenzulernen, schien es mir ein einfaches (gelesenes „gehasstes“) Produkt zu sein, aber als ich jeden Tag damit arbeitete, wurde mir klar, wie falsch ich war. Diesmal war ich also überrascht - es stellt sich heraus, dass GitLab über eine sehr umfangreiche API verfügt, die fast alle über die Benutzeroberfläche verfügbaren Funktionen abdeckt. Um unser Problem zu lösen, benötigen Sie jedoch nur vier Methoden, deren Namen für sich sprechen:
/ Benutzer ,
/ Gruppen ,
/ Projekte ,
/ Projekte /: ID / Probleme . GitlabService ist für die direkte Interaktion mit GitLab verantwortlich, und andere Klassen greifen darauf zu. Die Implementierung der updateGroups-Methode der GroupService-Klasse sieht beispielsweise folgendermaßen aus:
export class GroupService extends AbstractService { public async updateGroups() { for (const data of await this.app.gitlabService.getGroups()) {
Der Befehl
updateProjectsCommand ist dafür verantwortlich, Informationen vom Gitab abzurufen und die relevanten Informationen in unserer Datenbank zu aktualisieren, die über die Konsole aufgerufen werden können. Unsere Anwendung startet sie jedoch selbst mit der in der Konfiguration angegebenen Häufigkeit:
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) } } }
Wir werden die Anzahl, Farbe und den Namen der Spalten auf der Platine nicht fest codieren, sondern sie in config.js anpassbar machen. Wir haben fünf davon, aber für jemanden werden vielleicht nur zwei benötigt und saure Farbe:
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)' }, ],
Die Hauptsache ist, dass es mindestens zwei geben sollte, sonst wird die Anwendung nicht gestartet.
Um mit der Front zu interagieren, benötigen wir eine Methode, die Aufgaben aus der Datenbank empfängt und in „Spalten“ sortiert:
Show 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) {
Und ein Controller, der neue Aufgabenpositionen speichert:
Show 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. Wir sammeln Informationen von GitLab, bereiten Daten für die Anzeige des Boards vor und wissen sogar, wie aktualisierte Aufgabenpositionen auf dem Board gespeichert werden. Das einzige was noch übrig ist, ist das Board selbst zu machen. Und hier sind wir mit einem mächtigen Werkzeug bewaffnet:
Vue CLI - Anstelle von tausend Wörtern
TSX
Um das Refactoring, das Auffinden von Fehlern, das Flusen und andere Probleme zu erleichtern, verwenden wir Tsx-Vorlagen, die Vue.js
fast sofort unterstützt
* . Zum Beispiel die wichtigste Komponente in unserem Board, die Komponente der Aufgabenpräsentation:
Code anzeigen 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 }
Diese Komponente zeigt eine Taskleiste an. Beachten Sie, dass die Aufgabe tatsächlich Zeit aufgewendet und geplant hat:
Es gibt auch
Column.tsx- und
Kanban.tsx-Komponenten . Nur drei Komponenten bieten die Darstellung von Aufgaben an der Tafel:
Vue.Draggable , dessen Beine aus
SortableJS wachsen, ist für das Verschieben von Aufgaben auf dem Board verantwortlich. Es ist sehr einfach zu bedienen:
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> /* ... */ } }
Sicherlich haben Sie bemerkt, dass jeder Benutzer seine eigene Farbe hat, mit der das Login angezeigt wird. Dies ist sehr praktisch - unter einer Reihe von Aufgaben können Sie schnell Ihre eigenen finden. Es gibt zwei Möglichkeiten, um „Mehrfarben“ zu erreichen: die Farbe mit den Händen für jeden Benutzer festzulegen oder sie basierend auf etwas zu generieren.
Geben Sie mir Ihren Benutzernamen und ich sage Ihnen, welche Farbe Sie haben
Der Farbwähler der Benutzerklasse ist für die Farbgenerierung verantwortlich. Ich habe diese Funktionalität speziell im Backend implementiert, da ich Kryptografie benötigte und es ein Verbrechen ist, eine ganze Bibliothek für eine so kleine Aufgabe nach vorne zu ziehen. Und es ist richtiger, wenn die Rückseite und nicht die Vorderseite für die Eigenschaften von Entitäten verantwortlich ist:
export class User extends AbstractEntity { public get color() { const hash = crypto.createHash('md5').update(this.username).digest()
Für diejenigen, die wissen wollen, welche Farbe für sie bestimmt ist, war ich nicht zu faul
, um das Snippet auf CodePen zu schneiden .
Das ist alles
Der Arbeitsdienstag begann mit folgendem Dialog:
R: Nun, wie ist das Board fertig?
Ich: Ja, hier.
R: Oh, cool!
Das Ergebnis meines "Arbeitstages" liegt hier:
https://github.com/onlanta/kanban . Sie können es nicht nur sehen, berühren, sondern auch verwenden (Anweisungen dort). Und Sie können darauf einen ganzen Zeit-Tracker für Hitlab aufbauen, den wir in der Firma gemacht haben.
Warum habe ich beschlossen, diese Notiz zu schreiben? Dies ist einer der jüngsten Glanzmomente in meiner beruflichen Tätigkeit, als die Aufgabe wichtig, interessant und gleichzeitig recht einfach war und sich herausstellte, dass ihre Lösung einfach und elegant war. Ich schlage in den Kommentaren vor, ähnliche Aufgaben aus Ihrer Praxis in Erinnerung zu rufen.
Wo sind wir ohne freie Stellen?