Wprowadzenie do projektu QualitySpy
Transkrypt
Wprowadzenie do projektu QualitySpy
Wprowadzenie do projektu QualitySpy Na podstawie instrukcji implementacji prostej funkcjonalności. 1. Wstęp Celem tego poradnika jest wprowadzić programistę do projektu QualitySpy. Będziemy implementować krok po kroku prostą funkcjonalność związaną z serwisem GitHub. W trakcie tłumaczenia będę komentował i opisywał strukturę projektu. Dla uproszczenia będę pomijał początek nazwy pakietu pl.wroc.pwr.qualityspy i będę odnosił się do tylko ostatniego członu. 2. Co będzie implementowane Zaimplementujemy funkcjonalność wyświetlającą liczbę repozytoriów które posiada dany użytkownik na GitHubie i liczbę obserwujących dla każdego z repozytoriów. Instrukcja została oparta o przykładowe użycie biblioteki do obsługi API Githuba: https://github.com/eclipse/egit-github/tree/master/org.eclipse.egit.github.core 3. Rewizja Przykład jest tworzony w oparciu o najnowszą rewizję na dzień 28.06.2015. Jest to rewizja numer 185. 4. Nowe pole konfiguracyjne W tym punkcje zajmiemy się dodaniem nowego pola do plików konfiguracyjnych. Przejdźmy do pakietu common BugtrackerProperties z modułu common i wybierzmy interfejs Interfejsy XXXProperties wraz z klasą Properties definiują strukturę pliku konfiguracyjnego. Interfejsy opisują jakie dane można pobrać z konfiguracji, natomiast klasa dodatkowo opisuje jak te dane są zapisywane w pliku (mówiąc dokładniej definiuje nazwę klucz. Konfiguracja jest zapisywana w postaci klucz – wartość). Dodajmy zatem metodę o definicji public getGithubExampleUser. String getXXX(); np. o nazwie Zapisanie zmian w tym pliku powinno od razu skutkować wyświetleniem błędu w klasie Properties. Przejdźmy zatem do tej klasy. Jako że klasa implementuje interfejsy XXXProperties, musimy zaimplementować dodaną właśnie metodę. Jeśli spojrzymy na strukturę klasy zauważymy, że metody występują trójkami: getXXX(), setXXX(String value) i getXXXKey(). Pierwsza i druga kolejno pobiera i zapisuje wartość korzystając z metod getProperty(String key) i setProperty(String key, String value), trzecia metoda zwraca wartość klucza dla wybranej opcji konfiguracji (jak wcześniej wspomniałem dane zapisywane są w postaci klucz – wartość). Dodajmy zatem trójkę takich metod: Zdefiniujmy metodę public String getGithubExampleUser() która zwraca tekst „exampleUser”. Następnie zdefiniujmy metody getGithubExampleUser i setGithubExampleUser tak jak jest to opisane w komentarzu powyżej i posiłkując się przykładami z innych pól konfiguracyjnych np. z pola JiraPassword. Następnie musimy to pole konfiguracyjne obsłużyć przez interfejs graficzny. W tym celu przejdźmy do klasy ConfigJPanel z pakietu gui w modue gui. Rysunek 1 ConfigJPanel Ta klasa zawiera całą implementacje funcjonalności związaną z obsługą pól edycyjnych ich edycją i walidacją. Sam zapis wywoływany jest w klasie MenuJPanel, która wywołuje metodę saveChangesOfComponent klasy ConfigJPanel, która iteruje po wierszach tabeli widocznej na powyższym zrzucie ekranu, dzięki czemu dodanie do niej nowego wiersza z opcją konfiguracyjną spowoduje już zapisywanie tej opcji do pliku. Odnajdźmy metodę addRowJiraToConfigTable, metoda ta powoduje dodanie wierszy do tabeli edycyjnej po wybraniu zakładki Jira. Zakładka Jira obsługuje zarówno pobieranie danych z IssueTrackera Jiry jak i Githuba poprzez odpowiedni wybór opcji bugtracker. Metoda ta zawiera szereg wywołań metody addRowToConfigTable, która jako argumenty przyjmują klucz i wartość pola konfiguracyjnego. Wykorzystajmy zatem wcześniej stworzone metody getGithubExampleUserKey i getGithubExampleUser. Dodajmy zatem nowe wywołanie wyżej wspomnianej metody. Zbudujmy cały projekt i uruchommy go zgodnie z instrukcją przekazaną prze prowadzących zajęcia. (Niepoprawne uruchomienie np. poprzez wybranie Run ze środowiska programistycznego spowoduje nie pojawienie się i/lub błędne pojawienie się przykładowych konfiguracji) Wybierzmy jedną z przykładowych konfiguracji TestGitHub/1.0. Na zakładce Jira powinno pojawić się nowe pole konfiguracyjne: Rysunek 2 GUI z nowym polem Wprowadźmy przykładową wartość np. defunkt i naciśnijmy przycisk ENTER lub przejdźmy do innego pola konfiguracyjnego. Tylko w ten sposób zapiszemy zmiany w polu. Następnie wybierzmy ikonę Dyskietki. Konfiguracja jest za każdym razem czytana z pliku, dlatego niezbędne jest wcześniejsze zapisanie konfiguracji przed uruchomieniem funkcjonalności związanej ze ściąganiem danych. Przejdźmy do folderu config/TestGitHub/1.0 i otwórzmy w edytorze tekstu plik qs.properties. Rysunek 3 Ścieżka do pliku qs.properties Rysunek 4 Plik qs.properties 5. Implementacja przykładowej funkcjonalności Nasza przykładowa funkcjonalność wykorzystującym GitHuba. jest związana z komponentem bugtracker Funkcjonalność ściągająca zagadnienia dla wybranego repozytorium już jest zaimplementowana, jest tam obsłużone tworzenie klienta dla githuba i serwisu repozytoriów, dlatego właśnie tam małym kosztem dołożymy naszą przykładową funkcjonalnośc. Dla tych co nie zaglądali do przykładów biblioteki z linku w rozdziale Co będzie implementowane: Rysunek 5 Przykładowe użycie Github klienta Rozpocznijmy implementacje. Przejdźmy do modułu bugtracker bugtracker.github wybierzmy klasę GithubConnector. i z pakietu Moduły odpowiedzialne za ściąganie danych takie jak: bugtracker, metrics, version-control, ci zawierają klasy XXXConnector. Są to klasy których zadaniem jest zainicjowanie właściwego klienta z daną usługą np. bugtracker serwisu Github, połączenie z usługą Jenkins itp. i wystawienie metod pozwalających na pobranie danych z tych usług. Dane te muszą zostać także odpowiednio przekonwertowane na model który jest zapisywany w bazie danych.Dla przykładu GithubConnector w metodzie init tworzy połączenie z serwisem Github za pomocą klasy GithubClient, która pochodzi z zewnętrznej biblioteki. Konfiguruje to połączenie a następnie udostępnia metodę getIssuesWithFullDescription, która z kolei pobiera zagadnienia z githuba także za pomocą funkcjo z biblioteki service.getIssues(repository, issueFilter); a następnie wykorzystuje odpowiedni konwerter do konwersji na format modelu parser.convertGithubIssueToIssueDescription(issue). Dodajmy zatem do tej klasy pole przechowujące listę Repozytoriów wybranego użytkownika: private List<Repository> repos; A następnie w metodzie init po zainicjowaniu klienta Githuba i serwisu repozytoriów wykonajmy metodę z Rysunku: Przykładowe użycie Github klienta: repositoryService.getRepositories(String user) A skąd pobrać usera? Zauważmy, że do konstruktora GithubConnectora podawany jest obiekt klasy Properties, dokładnie tej klasy którą modyfikowaliśmy w poprzednim rozdziale, dzięki czemu mamy dostęp do metody getGithubExampleUser(). Przed wywołaniem sprawdźmy tylko czy przekazane pole nie jest puste. Rysunek 6 Kod po modyfikacjach Doskonale. Po tych modyfikacjach po utworzeniu GithubConnectora zostaje pobrana lista repozytoriów. Utwórzmy teraz metodę, która będzie wyświetlała odpowiednie informacje. Utwórzmy metodę o definicji: public void showWatchersList(); Metoda nie zwraca nic, ponieważ wykorzystamy klasę ErrorWindow przeznaczoną do wyświetlania błędów w GUI. Przeitreujmy po repozytoriach z listy repos i dla każdego z nich odczytajmy wartość zwracaną przez metodę getWatchers(). Dane pod postacią nazwa repozytorium – liczba wrzućmy do pojedynczego Stringa. W tym celu polecam użyć klasy StringBuilder, która jest klasą przeznaczoną właśnie do wielokrotnego sklejania łańcucha tekstowego. Można także na potrzeby ćwiczenia wykorzystać po prostu operator +, jednak zalecam zapoznać się z w/w klasą. Klasa ErrorWindow jest klasą typu Singleton. Aby uzyskać instancję klasy Singleton posłużymy się metodą statyczną ErrorWindows.getInstance(). Następnie aby wykorzystać ErrorWindow wykorzystamy metodę handleError. Ta metoda przyjmuje dwa argumenty typu Exception i typu Errors. Klasa Errors jest klasą zdefiniowaną w module common, tak jak i sama klasa ErrorWindow w pakiecie errorHandling. Klasa ta jest klasą abstrakcyjną i służy do implementacji zachowania przycisków TryAgain i EndProcess. Klasa ErrorWindow służy do wyświetlania komunikatu wyjątku, dlatego musimy opakować nasz tekst w jakiś wyjątek, dlatego posłużymy się wywołaniem new Exception(watchersList). Jeśli chodzi o drugi argument, należy przekazać nową instancję klasy Errors z pustą implementacją jej metod. Rysunek 7 Użycie ErrorWindow W tym momencie implementacja samej przykładowej funkcjonalności jest gotowa. Pozostaje nam tylko wywołanie. 6. Wywołanie nowej funkcjonalności Wróćmy do modułu gui, tym razem jednak do pakietu integration. W tym pakiecie znajdują się klasy FetchXXXThread. Te klasy poza klasą FetchDataThread, która ma inne znaczenie w metodzie fetcherRun implementuje to co się dzieje po wybraniu jednego z przycisków Fetch w GUI. Wspomniana klasa FetchDataThread to klasa która opakowuje klasy FetchXXXThread w odpowiedni wątek i obsługę błędów. Otwórzmy klasę FetchJiraThread. Jak wcześniej wspomniałem funkcjonalność ściągania zagadnień z bugtrackera Jira i Github są połączone ze sobą. W metodzie fetcherRun przejdźmy do warunku gdzie wykryte zostało że pole konfiguracyjne bugtracker ma wartość „github”. Zauważmy że tworzony jest GithubConnector na referencji klasy bazowej IBugTrackerConnector. Pamiętajmy że stworzona funkcjonalność pozwala nam wyciągnąć dane za pomocą metody, dodanej wyłącznie do klasy GithubConnector. Musimy zatem obsłużyć tę funkcjonalność w tym warunku. Na początku zrzutujmy referencję issueConnector na referencję klasy GithubConnector, a następnie wywołajmy metodę showWatchersList();, ponownie pod warunkiem, że pole githubExampleUser nie jest puste. Rysunek 8 Kod po modyfikacjach. Ponownie zbudujmy projekt i uruchommy według instrukcji podanej na zajęciach. Wybierzmy konfigurację TestGitHub/1.0. Przejdźmy na zakładkę Jira i wprowadźmy exampleUser: „defunkt”. Pamiętajmy że musimy nacisnąć ENTER lub wyjść z pola edycyjnego, a następnie nacisnąć ikonę zapisu. Wybierzmy przycisk Fetch JIRA. po chwili powinno pojawić się okno błędu z listą repozytoriów i liczbą ich obserwatorów. Rysunek 9 Okno błędu