genofire/hs_monolith
genofire
/
hs_monolith
Archived
1
0
Fork 0

[Task]: Documentation and Test

This commit is contained in:
mlabusch 2017-05-15 10:22:24 +02:00
parent 8902066812
commit 2374f9331b
29 changed files with 109 additions and 100 deletions

View File

@ -34,7 +34,7 @@ func main() {
config = models.ReadConfigFile(configFile) config = models.ReadConfigFile(configFile)
// Config packages: // Config packages:
web.GoodAvailablityTemplate = config.GoodAvailablityTemplate web.GoodAvailabilityTemplate = config.GoodAvailabilityTemplate
web.GoodFreshnessTemplate = config.GoodFreshnessTemplate web.GoodFreshnessTemplate = config.GoodFreshnessTemplate
runtime.CacheConfig = config.CacheClean runtime.CacheConfig = config.CacheClean
runtime.ProductURL = config.MicroserviceDependencies.Product runtime.ProductURL = config.MicroserviceDependencies.Product

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

View File

@ -14,21 +14,25 @@
\section*{Handout Admin-Frontend Warenwirtschaft} \section*{Handout Admin-Frontend Warenwirtschaft}
\begin{figure}[H]
\begin{center}
\includegraphics[width=0.65 \textwidth]{./dummy.png}
\end{center}
\caption{Übersicht des Admin-Frontends}
\end{figure}
\begin{itemize} \begin{itemize}
\item \textit{Hinzufügen:} Neue Waren in den Bestand aufnehmen \item \textit{List:} Auflistung aller Produkte mit ihrem Warenbestand
\item \textit{Entfernen:} Waren manuell aus dem Bestand entfernen \item \textit{Produktseite:} Über einen Klick auf ein Produkt unter \textit{List} gelangt man auf seine Seite, hier werden die zugehörigen Waren angezeigt
\item \textit{...:} ... \item \textit{Hinzufügen:} Neue Waren können über den Button mit dem Pluszeichen in den Bestand aufgenommen werden
\item \textit{Entfernen:} Waren werden manuell, über den Mülleimer-Icon auf ihren Produktseiten gelöscht
\item \textit{Statistic:} Anzeige des gesamten und des durchschnittlichen Warenbestandes
\end{itemize} \end{itemize}
\begin{figure}[H]
\centering
\includegraphics[width=1 \textwidth]{./product_handout.png}
\caption{Produktübersicht}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[width=1 \textwidth]{./add_handout.png}
\caption{Hinzufügen von Waren}
\end{figure}
\end{document} \end{document}

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

View File

@ -1,7 +1,7 @@
\section{Definition der Anforderungen} \section{Definition der Anforderungen}
\label{sec: Definition der Anforderungen} \label{sec: Definition der Anforderungen}
Der Microservice Warenwirtschaft dient der Verwaltung der Warenbestände für den Webshop Mosh. Er ermöglicht es zum Beispiel, dass neue Waren erfasst werden können und keine Waren verkauft werden, die sich nicht mehr im Warnbestand befinden. Die nachfolgende Tabelle \ref{tabl:Begriffe} definiert die hier verwendeten Begriffe, so wie sie in dem Code und innerhalb dieser Dokumentation verwendet werden. Der Microservice Warenwirtschaft dient der Verwaltung der Warenbestände für den Webshop Mosh. Er ermöglicht es zum Beispiel, dass neue Waren erfasst werden können und keine Waren verkauft werden, die sich nicht mehr im Warnbestand befinden. Die nachfolgende Tabelle \ref{tabl:Begriffe} definiert die hier verwendeten Begriffe, so wie sie in dem Code und innerhalb dieser Dokumentation genutzt werden.
\begin{table}[H] \begin{table}[H]
\begin{small} \begin{small}
\begin{center} \begin{center}
@ -26,12 +26,12 @@ Der Microservice Warenwirtschaft dient der Verwaltung der Warenbestände für de
\textit{\textit{Dieser Microservice ist Teil der Prüfungsleistung in den Modul KSS im Masterstudiengang komplexe Softwaresysteme des Sommersemesters 2017 an der Hochschule Bremen. Zu der gestellten Aufgabenstellung gehört nicht, den Microservice zusammen mit den Microservices der anderen Gruppen in einen gemeinsamen, lauffähigen Webshop zu integrieren.}} \textit{\textit{Dieser Microservice ist Teil der Prüfungsleistung in den Modul KSS im Masterstudiengang komplexe Softwaresysteme des Sommersemesters 2017 an der Hochschule Bremen. Zu der gestellten Aufgabenstellung gehört nicht, den Microservice zusammen mit den Microservices der anderen Gruppen in einen gemeinsamen, lauffähigen Webshop zu integrieren.}}
\newpage \newpage
Die übergeordnete Aufgabe dieses Microservice ist die Speicherung der Waren mit ihrem Lagerort sowie einem Zeitstempel, wann sie erfasst wurden. Nachfolgend werden die weiteren, detaillierten Anforderungen an diesen Microservice zusammengefasst. Die übergeordnete Aufgabe dieses Microservice ist die Speicherung der Waren mit ihrem Lagerort sowie einem Zeitstempel, wann sie ablaufen. Nachfolgend werden die weiteren, detaillierten Anforderungen an diesen Microservice zusammengefasst.
\begin{itemize} \begin{itemize}
\item \textbf{Funktionen des Admin-Frontends} \item \textbf{Funktionen des Admin-Frontends}
\begin{itemize} \begin{itemize}
\item Hinzufügen neuer Waren zum Warenbestand \item Hinzufügen neuer Waren (maximal 100 Stück auf einmal) zum Warenbestand
\item Manuelles Entfernen von Waren aus dem Warenbestand, zum Beispiel wenn diese verdorben sind \item Manuelles Entfernen von Waren aus dem Warenbestand, zum Beispiel wenn diese verdorben sind
\item Entfernen von einzelnen Waren aus dem Warenbestand, wenn diese an einen Kunden versendet werden \item Entfernen von einzelnen Waren aus dem Warenbestand, wenn diese an einen Kunden versendet werden
\item Blockieren von Waren in dem Warenbestand, wenn ein Kunde sie in seinen Warenkorb gelegt hat \item Blockieren von Waren in dem Warenbestand, wenn ein Kunde sie in seinen Warenkorb gelegt hat
@ -40,13 +40,10 @@ Die übergeordnete Aufgabe dieses Microservice ist die Speicherung der Waren mit
\item \textbf{Funktionen des Kunden-Frontends} \item \textbf{Funktionen des Kunden-Frontends}
\begin{itemize} \begin{itemize}
\item Anzeige des Warenbestands über ein Ampelsystem \item Anzeige des Warenbestands über ein Ampelsystem
\item Ein Warenbestand größer sieben entspricht der Farbe grün (ausreichende Anzahl vorhanden)
\item Ein Warenbestand zwischen vier und sieben entspricht der Farbe orange (moderate Anzahl vorhanden)
\item Ein Warenbestand zwischen null und drei entspricht der Farbe rot (geringe Anzahl vorhanden)
\end{itemize} \end{itemize}
\item \textbf{Optionale Zusatzfunktionen} \item \textbf{Optionale Zusatzfunktionen}
\begin{itemize} \begin{itemize}
\item Ausgabe einer Statistik, wie viele Waren sich gesamt und durchschnittlich im Warenbestand befinden im Admin-Fontend \item Ausgabe einer Statistik, wie viele Waren sich gesamt und durchschnittlich im Warenbestand befinden im Admin-Fontend
\item Ampeldarstellung pro Ware, die Anzeigt ob diese bereits überaltert ist, im Admin-Frontend (ein Alter von mehr als X Tagen wird als überaltert angesehen) \item Ampeldarstellung pro Ware, die Anzeigt ob diese bereits ihr angegebenes Ablaufdatum erreicht hat, im Admin-Frontend
\end{itemize} \end{itemize}
\end{itemize} \end{itemize}

View File

@ -1,5 +1,6 @@
\section{Dokumentationsstruktur} \section{Dokumentationsstruktur}
\label{sec: Dokumentationsstruktur} \label{sec: Dokumentationsstruktur}
Für die Dokumentation des Microservice Warenwirtschaft wurden eine Kombination aus zwei Dokumenten gewählt. Zum einen beschreibt ein Handout auf einer Seite die Funktionen des Admin-Frontends für den Benutzer. Diese sehr kurze Dokumentenform wurde gewählt, da Benutzer häufig nicht gewillt sind, umfangreiche Anleitungen zu lesen um eine Anwendung nutzen zu können. Vielmehr wollen sie schnell einen Überblick der Kernfunktionalitäten erhalten. Zu diesem Zweck arbeitet das Handout mit einem aufbereiteten Screenshot des Admin-Frontends und einer Beschreibung der Funktionen in Stichpunkten. \par Für die Dokumentation des Microservice Warenwirtschaft wurden eine Kombination aus drei Dokumenten gewählt. Zum einen beschreibt ein bebildertes Handout auf einer Seite die Funktionen des Admin-Frontends für den Benutzer. Diese sehr kurze Dokumentenform wurde gewählt, da Benutzer häufig nicht gewillt sind, umfangreiche Anleitungen zu lesen um eine Anwendung nutzen zu können. Vielmehr wollen sie schnell einen Überblick der Kernfunktionalitäten erhalten.\par
Auf der anderen Seite muss der Microservie auch für Entwickler dokumentiert sein, hierfür wurde diese Dokumentation angelegt. Sie beginn anstelle eines Abstract mit einem Steckbrief des Microservice, der dessen grundlegende Struktur und Funktionalität kurz beschreibt. In dem eigentlichen Dokument werden dann zunächst die Anforderungen an den Microservice beschrieben, da ihre Umsetzung das primäre Ziel der Entwicklung ist. Weiter werden der Microservice mit seinem Aufbau, den Schnittstellen und der Anpassung des gegebenen Monolithen beschrieben. Es folgt die Dokumentation von Implementierungsregeln und der gewählten Blackbox-Testfälle. Der Anhang dieser Dokumentation umfasst einen Anleitung für den Start des Microservice, das Handout des Admin-Frontends sowie ein beispielhaftes Testprotokoll. Auf Details wir ein Abkürzungs- oder Literaturverzeichnis wurde in dieser Dokumention bewusst verzichtet, um sie kurz zu halten. Zitate und Verweise werden hier in Form von Fußnoten integriert. Auf der anderen Seite muss der Microservie auch für Entwickler dokumentiert sein, hierfür wurde diese Dokumentation angelegt. Sie beginn anstelle eines Abstract mit einem Steckbrief des Microservice, der dessen grundlegende Struktur und Funktionalität kurz beschreibt. In dem eigentlichen Dokument werden dann zunächst die Anforderungen an den Microservice beschrieben, da ihre Umsetzung das primäre Ziel der Entwicklung ist. Weiter werden der Microservice mit seinem Aufbau, den Schnittstellen und der Anpassung des gegebenen Monolithen sowie Implementierungsregeln beschrieben. Dieses Dokument schließt mit einem \textit{Getting Started} Guide. Auf Details wir ein Abkürzungs- oder Literaturverzeichnis wurde in dieser Dokumention bewusst verzichtet, um sie kurz zu halten. Zitate und Verweise werden hier in Form von Fußnoten integriert.\par
Abschließend dokumentiert das Testprotkoll, als drittes Dokument, die für diesen Microservice angewendet Black-Box-Testfälle - das heißt jene Tests die anhand der Anforderungen und aus Sicht des Benutzer durchgeführt wurden.

View File

@ -1,17 +1,12 @@
\section{Implementierungsregeln} \section{Implementierungsregeln}
\label{sec: Implementierungsregeln} \label{sec: Implementierungsregeln}
Die folgende Aufzählung gibt einige Regeln für die Implementierung des Microservice Warenwirtschaft vor. Diese sollten im Rahmen einer Weiterentwicklung eingehalten werden, um die Konsistenz des Codes aufrecht zu erhalten. Die folgende Aufzählung gibt einige Regeln für die Implementierung des Microservice Warenwirtschaft vor. Diese sollen im Rahmen einer Weiterentwicklung eingehalten werden, um die Konsistenz des Codes aufrecht zu erhalten.
\begin{enumerate} \begin{enumerate}
\item Packages werden eindeutig und sprechend benannt \item Packages werden eindeutig und sprechend benannt
\item Go-Files werden eindeutig und sprechend benannt \item Go-Files werden eindeutig und sprechend benannt
\item Wenn ein Package nur ein Go-File enthält, erhält dieses den Namen seines Packages \item Wenn ein Package nur ein Go-File enthält, erhält dieses den Namen seines Packages
\item Vor jedem Package steht ein ein- bis zweizeiliger, beschreibender Kommentar, der die Hauptfunktionalitäten wiedergibt \item Vor jedem Package steht ein ein- bis zweizeiliger, beschreibender Kommentar, der die Hauptfunktionalitäten wiedergibt
\item Vor jeder Funktion steht ein zwei- bis dreizeiliger, beschreibender Kommentar, dieser enthält \item Vor jeder Funktion steht ein ein- bis zweizeiliger, beschreibender Kommentar, der die Hauptfunktionalitäten wiedergibt
\begin{enumerate}
\item eine ein- bis zweizeilige Beschreibung der Funktionalität
\item eine einzeilige Beschreibung der Eingabe- und Rückgabewerte (entfällt, wenn diese nicht vorhanden sind)
\end{enumerate}
\item Aus Gründen der Übersichtlichkeit werden Variablen und Structs werden nur mit vorangestellten Kommentaren versehen, wenn sie nicht selbsterklärend sind
\end{enumerate} \end{enumerate}

View File

@ -3,7 +3,7 @@
\begin{figure}[H] \begin{figure}[H]
\begin{center} \begin{center}
\includegraphics[width=0.65 \textwidth]{./pics/struktur.png} \includegraphics[width=0.95 \textwidth]{./pics/struktur.png}
\end{center} \end{center}
\caption{Microservice Warenwirtschaft} \caption{Microservice Warenwirtschaft}
\label{pic: Microservice Warenwirtschaft} \label{pic: Microservice Warenwirtschaft}
@ -11,11 +11,11 @@
\begin{itemize} \begin{itemize}
\item Der Microservice Warenwirtschaft speichert die einzelnen Waren pro Produkt mit ihrem Lagerort und einem Zeitstempel \item Der Microservice Warenwirtschaft speichert die einzelnen Waren pro Produkt mit ihrem Lagerort und Ablaufdatum
\item Das Admin-Frontend erlaubt das Hinzufügen sowie manuelle Löschen von Waren aus dem Warenbestand und zeigt zusätzlich eine Übersicht der Warenbestände \item Das Admin-Frontend erlaubt das Hinzufügen sowie manuelle Löschen von Waren aus dem Warenbestand und zeigt zusätzlich eine Übersicht der Warenbestände
\item In dem Kunden-Frontend wird der Warenbestand durch ein Ampelsystem dargestellt \item In dem Kunden-Frontend wird der Warenbestand durch ein Ampelsystem dargestellt
\item Der Microservice wurde in Go entwickelt, die Abbildung \ref{pic: Microservice Warenwirtschaft} gibt einen Überblick der Package-Struktur \item Der Microservice wurde in Go entwickelt, die Abbildung \ref{pic: Microservice Warenwirtschaft} gibt einen Überblick der Package-Struktur
\item Der statische Inhalt der Webseite ist in dem Package \texttt{webroot} verordnet \item Der statische Inhalt der Webseite ist in dem Package \texttt{webroot} verordnet
\item Als Datenbank wird eine In-Memory-Datenbank im Cache verwendet (Package \texttt{lib})
\item Die Hauptfunktionalitäten, die zentralen Structs sowie die notwendigen Hilfsfunktionen sind in den Packages \texttt{http} und \texttt{models} verordnet \item Die Hauptfunktionalitäten, die zentralen Structs sowie die notwendigen Hilfsfunktionen sind in den Packages \texttt{http} und \texttt{models} verordnet
\item Eine SQL-Lite-Datenbank stellt den Persitant Layer
\end{itemize} \end{itemize}

View File

@ -1,6 +1,6 @@
\section{Architektur des Microservice} \section{Architektur des Microservice}
\label{sec: Architektur des Microservice} \label{sec: Architektur des Microservice}
Der Microservice Warenwirtschaft wurde in der Programmiersprache Go\footnote{https:\//golang.org\/doc\/} entwickelt. Go-An-wendungen bestehen aus Packages, in denen die einzelnen Go-Files organisiert sind, Klassen im Sinne der Objektorientierung gibt es nicht. Der Microservice Warenwirtschaft sett sich aus den neun Packages zusammen, die in der Abbildung X dargestellt werden. In den beiden nachfolgenden Unterkapiteln werden die Packages und die darin enthaltenen Go-Files des Presentation sowie des Application Layers kurz vorgestellt. Go-Files mit der Bezeichnung \texttt{<<Name>>\_test.go} beinhalten Whitebox-Testfälle um die Funktionen der benannten Go-Files zu prüfen. Aus Gründen der Übersichtlichkeit werden diese File hier nicht explizit aufgeführt. Der Microservice Warenwirtschaft wurde in der Programmiersprache Go\footnote{https:\//golang.org\/doc\/} entwickelt. Go-An-wendungen bestehen aus Packages, in denen die einzelnen Go-Files organisiert sind, Klassen im Sinne der Objektorientierung gibt es nicht. Der Microservice Warenwirtschaft setzt sich aus den neun Packages zusammen, die in der Abbildung \ref{pic:Struktur des Microservice} dargestellt werden. In den nachfolgenden Unterkapiteln \ref{subsec: Presentation Layer} und \ref{subsec: Application Layer} werden die Packages und die darin enthaltenen Go-Files des Presentation sowie des Application Layers kurz vorgestellt. Go-Files mit der Bezeichnung \texttt{<<Name>>\_test.go} beinhalten Whitebox-Testfälle um die Funktionen der benannten Go-Files zu prüfen. Aus Gründen der Übersichtlichkeit werden diese Files hier nicht explizit aufgeführt. Die weiteren Unterkapitel beschreiben die Schnittstellen, den Persistant Layer sowie die Log-Level, das Admin-Frontend und schließlich die Anpassung des Monolithen,um den Microservice Warenwirtschaft in diesen zu integrieren.
\begin{figure}[H] \begin{figure}[H]
\centering \centering
@ -12,7 +12,9 @@ Der Microservice Warenwirtschaft wurde in der Programmiersprache Go\footnote{htt
\newpage \newpage
\subsection{Schnittstellen zu anderen Microservices} \subsection{Schnittstellen zu anderen Microservices}
\label{subsec: Schnittstellen zu anderen Microservices} \label{subsec: Schnittstellen zu anderen Microservices}
Der Microservice Warenwirtschaft weißt vier Schnittstellen\footnote{Da es nicht Teil der übergeordneten Aufgabenstellung war, die Microservices der einzelnen Projektgruppen zu einem lauffähigen Webshop zusammenzufügen, greift der Microservice Warenwirtschaft an diesen Stellen auf Testdaten zurück} zu anderen Microservices auf. Zum einen sollte für die Authentifizierung der Benutzer des Admin-Frontends auf den Microservice Benutzerauthentifizierung zurückgegriffen werden. Außerdem benötigt der Microservice Informationen darüber, ob ein Benutzer eine Ware in den Warenkorb gelegt hat und ob eine Bestellung abgeschlossen wurde, sodass ie darin enthaltenen Waren aus dem Warenbestand gelöscht werden müssen. Somit entstehen ebenfalls Schnittstellen zu den Microservices Bestellung und Versandt. Abschließend weißt dieser Microservice eine Schnittstelle zu dem Microservice Produktkatalog auf, von welchem die angebotenen Produkte -- die sich dementsprechend im Lager befinden können -- abgefragt werden. Die drei nachfolgenden Listings zeigen die Daten, die von den Schnittstellen im JSON-Format erwartet, beziehungsweise an diese ausgegeben werden. Der Microservice Warenwirtschaft weißt drei Schnittstellen\footnote{Da es nicht Teil der übergeordneten Aufgabenstellung war, die Microservices der einzelnen Projektgruppen zu einem lauffähigen Webshop zusammenzufügen, greift der Microservice Warenwirtschaft an diesen Stellen auf Testdaten zurück} zu anderen Microservices auf. Zunächst soll für die Authentifizierung der Benutzer des Admin-Frontends vollständig auf den Microservice Benutzerauthentifizierung zurückgegriffen werden. Anstelle einer Login-Maske weißt das Admin-Frontend deshalb bisher nur einen Icon in Form eines Schlosses auf. Dieser symbolisiert, ob ein Benutzer die passende Berechtigung für das Admin-Front besitzt (Schloss geschlossen) oder nicht (Schloss geöffnet). \par
Weiter benötigt der Microservice Warenwirtschaft Informationen darüber, ob ein Benutzer eine Ware in den Warenkorb gelegt hat und ob eine Bestellung abgeschlossen wurde. So können Waren im Warenkorb für die Bestellung durch andere Benutzer blockiert und die erfolgreich bestellten Waren aus dem Warenbestand gelöscht werden. Diese Funktionalitäten geben eine eine Schnittstelle zu dem Microservice Bestellung vor. \par
Die dritte Schnittstelle besteht zu dem Microservice Produktkatalog, von welchem die angebotenen Produkte -- die sich dementsprechend im Lager befinden können -- abgefragt werden. Die drei nachfolgenden Listings zeigen die Daten, die von den der Microservices Produktkatalog und Bestellung im JSON-Format erwartet, beziehungsweise an diese ausgegeben werden.
\begin{lstlisting}[caption=Datenabfrage aus dem Produktkatalog] \begin{lstlisting}[caption=Datenabfrage aus dem Produktkatalog]
{ {
@ -40,21 +42,40 @@ Der Microservice Warenwirtschaft weißt vier Schnittstellen\footnote{Da es nicht
\newpage \newpage
\subsection{Presentation Layer} \subsection{Presentation Layer -- Admin-Frontend}
\label{subsec: Presentation Layer} \label{subsec: Presentation Layer}
Der Presentation Layer umfasst alle Packages, die sich mit der eigentlichen Darstellung der Warenwirtschaft aus der Sicht des Endbenutzers befassen. Im Detail ist dies das Package \textbf{\texttt{webroot}}, welches den statischen Inhalt der Frontends, wie zum Beispiel die HTML-Files und Bilder enthält. Der Presentation Layer umfasst alle Packages, die sich mit der eigentlichen Darstellung der Warenwirtschaft aus der Sicht des Endbenutzers befassen. Im Detail ist dies das Package \textbf{\texttt{webroot}}, welches den statischen Inhalt der Frontends, wie zum Beispiel die HTML-Files und Bilder enthält. \par
Die Startseite \textit{List} des Admin-Frontends zeigt eine Übersicht aller vorhandenen Produkte mit ihrer Anzahl an Waren (Abbildung \ref{pic:Admin-Frontend -- List}). Letztere wird mit einem Ampelsystem dargestellt, wobei ein vollkommen rot gefärbter Kreis einem Warenbestand von null entspricht und der Kreis mit zunehmender Anzahl an Waren immer mehr grün eingefärbt wird. Zu jeden Produkt kann über den, mit einem Plus, gekennzeichneten Button Waren hinzugefügt werden. Ein Klick auf das jeweilige Produkt führt zu dessen Produktseite. \par
Die Produktseiten führen die ID, die Gesamtanzahl an Waren sowie die einzelnen Waren auf (Abbildung \ref{pic:Admin-Frontend -- Produktseite}). Diese können jeweils über den Icon in Form eines Mülleimers manuell gelöscht werden. Auch auf den Produktseiten können über einen, mit einem Plus gekennzeichneten, Button neue Waren hinzugefügt werden. Beim Hinzufügen von neuen Waren sind für diese ein Ablaufdatum, eine Lagerposition sowie ein Kommentar und die Anzahl anzugeben (Abbildung \ref{pic:Admin-Frontend -- Hinzufuegen von Waren}). Um den Microservice auch in anderen Einsatzgebieten, als einem Webshop für Obst- und Gemüse einsetzen zu können, sind hier nur die Felder Lagerposition und Anzahl verpflichtend. Die Seite \textit{Statistics} gibt letztendlich einen Überblick der gesamten und der durchschnittlichen Waren im Warenbestand.
\begin{figure}[H]
\centering
\includegraphics[width=0.65 \textwidth]{./pics/product.png}
\caption{Admin-Frontend -- Produktseite}
\label{pic:Admin-Frontend -- Produktseite}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[width=0.65 \textwidth]{./pics/add.png}
\caption{Admin-Frontend -- Hinzufügen von Waren}
\label{pic:Admin-Frontend -- Hinzufuegen von Waren}
\end{figure}
\newpage
\subsection{Application Layer} \subsection{Application Layer}
\label{subsec: Application Layer} \label{subsec: Application Layer}
Die Packages des Application Layers umfassen die Logik des Microservice Warenwirtschaft. Sie werden nachfolgend aufgelistet und kurz beschrieben.
\paragraph{cmd:} Go-File main.go, welches alle Angaben zu den Config-Files der Applikation enthält \paragraph{cmd:} Go-File main.go, welches die Applixation letztendlich ausführt und alle Angaben zu den Config-Files der Applikation enthält
\paragraph{http:} Go-Files, die die Anwendungslogik (Funktionen) und die API-Rounten beinhalten. \paragraph{http:} Go-Files, die die Anwendungslogik (Funktionen) und die API-Routen beinhalten.
\begin{itemize} \begin{itemize}
\item \texttt{bindapi.go}: Funktionen, die für das Binden der URL-Pfade notwendig sind \item \texttt{bindapi.go}: Funktionen, die für das Binden der URL-Pfade notwendig sind
\item \texttt{good.go}: Funktionen fpr das Hinzufügen von Waren zum Warenbestand \item \texttt{good.go}: Funktionen für das Hinzufügen von Waren zum Warenbestand
\item \texttt{good\_show.go}: Funktionen für die Auflistung und Zählung der vorhandenen Waren sowie die Feststellung ihrer Verfügbarkeit zusammen \item \texttt{good\_show.go}: Funktionen für die Auflistung und Zählung der vorhandenen Waren sowie die Feststellung ihrer Verfügbarkeit
\item \texttt{good\_temp.go}: Hilfsfunktionen, die für die Darstellung des Warenbestandes als Ampel im Kunden-Frontend benötigt werden \item \texttt{good\_temp.go}: Hilfsfunktionen, die für die Darstellung des Warenbestandes als Ampel im Kunden-Frontend benötigt werden
\item \texttt{status.go}: Funktion, die den Status des Microservice abfragt \item \texttt{status.go}: Funktion, die den Status des Microservice abfragt
\end{itemize} \end{itemize}
@ -64,7 +85,7 @@ Der Presentation Layer umfasst alle Packages, die sich mit der eigentlichen Dar
\begin{itemize} \begin{itemize}
\item \texttt{config.go}: Structs mit den Informationen zur Konfiguration des Webservers, der Datenbank und dem Cache-Management sowie Hilfsfunktionen zum Lesen von Config-Files \item \texttt{config.go}: Structs mit den Informationen zur Konfiguration des Webservers, der Datenbank und dem Cache-Management sowie Hilfsfunktionen zum Lesen von Config-Files
\item \texttt{duration.go}: Structs und Hilfsfunktionen zur Definition eines Typs für Zeitangaben \item \texttt{duration.go}: Structs und Hilfsfunktionen zur Definition eines Typs für Zeitangaben
\item \texttt{good.go}: Structs und Hilfsfunktionen zur Darstellung von Waren, hier werden auch die beschriebenen Funktionalitäten wie das Blockieren von Waren beschrieben \item \texttt{good.go}: Structs und Hilfsfunktionen zur Darstellung von Waren, hier werden auch die geforderten Funktionalitäten wie das Blockieren von Waren umgesetzt
\item \texttt{structstorage}: \item \texttt{structstorage}:
\end{itemize} \end{itemize}
@ -73,8 +94,8 @@ Der Presentation Layer umfasst alle Packages, die sich mit der eigentlichen Dar
\begin{itemize} \begin{itemize}
\item \texttt{auth.go}: Hilfsfunktionen zur Prüfung, ob eine Berechtigung für den Zugriff vorliegt \item \texttt{auth.go}: Hilfsfunktionen zur Prüfung, ob eine Berechtigung für den Zugriff vorliegt
\item \texttt{cache\_worker.go}: Hilfsfunktionen für das Löschen und Anlegen von Cache-Workers \item \texttt{cache\_worker.go}: Hilfsfunktionen für das Löschen und Anlegen von Cache-Workers
\item \texttt{good\_release.go}: Hilfsfunktionen zum Blockieren und Entsperren Waren \item \texttt{good\_release.go}: Hilfsfunktionen zum Blockieren und Entsperren von Waren
\item \texttt{productcache.go}: Hilfsfunktionen zum Anlegen eines Caches für Produkte und zur Prüfung \item \texttt{productcache.go}: Hilfsfunktionen zum Anlegen eines Caches für Produkte
\item \texttt{runtime.go}: Übergreifende Hintergrundfunktionalitäten \item \texttt{runtime.go}: Übergreifende Hintergrundfunktionalitäten
\end{itemize} \end{itemize}
@ -87,41 +108,42 @@ Der Presentation Layer umfasst alle Packages, die sich mit der eigentlichen Dar
\item \texttt{database}: Go-File \texttt{database.go} mit Funktionen für das Öffnen und Schließen der Datenbank \item \texttt{database}: Go-File \texttt{database.go} mit Funktionen für das Öffnen und Schließen der Datenbank
\item \texttt{http}: Go-Files, die die Webserverlogik umgesetzten \item \texttt{http}: Go-Files, die die Webserverlogik umgesetzten
\begin{itemize} \begin{itemize}
\item \texttt{io.go}: Funktionen zum Lesen und Schreiben von JSON aus beziehungsweise in HTTP-PAckete \item \texttt{io.go}: Funktionen zum Lesen und Schreiben von JSON aus beziehungsweise in HTTP-Pakete
\item \texttt{permission.go}: Funktionen zur Prüfung der Berechtigung für den Zugriff \item \texttt{permission.go}: Funktionen zur Prüfung der Berechtigung für den Zugriff
\end{itemize} \end{itemize}
\item \texttt{log}: Go-File \texttt{log.go}, das den Logger startet und initiiert \item \texttt{log}: Go-File \texttt{log.go}, das den Logger startet und initiiert
\item \texttt{worker}: Go-File \texttt{worker.go}, dass Funktionen für die Nutzung des Caches für die Produkte aus dem Produktkatalog (Worker) bereitstellt \item \texttt{worker}: Go-File \texttt{worker.go}, dass Funktionen für die Nutzung eines Caches für die Produkte aus dem Produktkatalog (Worker) bereitstellt
\end{itemize} \end{itemize}
\subsection{Integrierte Tests}
\label{subsec: Integrierte Test}
Neben den Go-Files die bereits Whitebox-Tests enthalten, ist in dem Package \textbf{\texttt{test}} ein weiteres Go-File (\texttt{testRest.go}) enthalten. Dieses setzt einen Test des Webservers um, bei dem auf Testdaten eines Produktkataloges zurückgegriffen wird. Mit Hilfe der integrierten Test kann in der hier beschriebenen Version eine Code-Coverage von 100\% erreicht werden, das heißt jedes Stück Code wird mindestens einmal zur Ausführung gebracht.
\newpage
\subsection{Persistant Layer} \subsection{Persistant Layer}
Der Persitant Layer umfasst eine SQL-Lite-Datenbank, die im Cache gehalten wird. Die nachfolgende Abbildung \ref{pic:Datenbankmodell des Microservice} zeigt den grundsätzlichen Aufbau der Datenbank. Sie speichert den Warenbestand (stock) in Produkten (product). Jedes Produkt wird mit seiner ID und seinem Namen gehalten, die aus dem Produktkatalog bezogen und in einem Cache zwischengespeichert werden. Zu jedem Produkt gehören wiederum mehrere Waren (good), die ein Lieferdatum und eine Anzahl haben. Dabei kann eine Ware nur zu einem Produkt gehören. Der Persitant Layer umfasst eine SQL-Lite-Datenbank, die im Cache gehalten wird. Die nachfolgende Abbildung \ref{pic:Datenbankmodell des Microservice} zeigt den grundsätzlichen Aufbau der Datenbank. Sie speichert den Warenbestand (stock) in Produkten (product). Jedes Produkt wird mit seiner ID und seinem Namen gehalten, die aus dem Produktkatalog bezogen und in einem Cache zwischengespeichert werden. Zu jedem Produkt gehören wiederum mehrere Waren (good), die eine ID, ein Ablaufdatum und eine Lagerposition besitzen. Dabei kann eine Ware nur zu einem Produkt gehören.
\begin{figure}[H] \begin{figure}[H]
\centering \centering
\includegraphics[width=0.5 \textwidth]{./pics/db.pdf} \includegraphics[width=0.65 \textwidth]{./pics/db.pdf}
\caption{Datenbankmodell des Microservice} \caption{Datenbankmodell des Microservice}
\label{pic:Datenbankmodell des Microservice} \label{pic:Datenbankmodell des Microservice}
\end{figure} \end{figure}
\subsection{Integrierte Tests}
\label{subsec: Integrierte Test}
Neben bisherigen Packages, die bereits Whitebox-Tests umfassen, ist in dem Package \textbf{\texttt{test}} ein weiteres Go-File (\texttt{testRest.go}) enthalten. Dieses setzt einen Test des Webservers um, bei dem auf Testdaten eines Produktkataloges zurückgegriffen wird. Mit Hilfe der integrierten Tests kann in der hier beschriebenen Version eine Code-Coverage von 100\% erreicht werden, das heißt jedes Stück Code wird mindestens einmal zur Ausführung gebracht.
\newpage \newpage
\subsection{Anpassung des Monolithen} \subsection{Anpassung des Monolithen}
\label{subsec: Anpassung des Monolithen} \label{subsec: Anpassung des Monolithen}
Damit der Microservice Warenwirtschaft durch den bestehenden Monolithen des Webshops Mosh genutzt werden kann, wurden hier eingei Änderungen vorgenommen. Zunächst wurde für den Aufruf des Admin-Frontends über die URL des Monolithen \texttt{/admin} das nachfolgende Mapping in der Java-Datei \texttt{HomepageController.java} ergänzt. Damit der Microservice Warenwirtschaft durch den bestehenden Monolithen des Webshops Mosh genutzt werden kann, wurden hier dir nachfolgend aufgeführten Änderungen vorgenommen. Zunächst wurde für den Aufruf des Admin-Frontends über die URL des Monolithen \texttt{/stockadmin} das nachfolgende Mapping in der Java-Datei \texttt{HomepageController.java} ergänzt.
\begin{lstlisting}[caption=Datenabfrage von der Bestellung (Waren wurden bestellt), language=Java] \begin{lstlisting}[caption=Datenabfrage von der Bestellung (Waren wurden bestellt), language=Java]
@RequestMapping(value = "/admin", method = RequestMethod.GET) @RequestMapping(value = "/stockadmin", method = RequestMethod.GET)
public String redirect(Model model) { public String redirect(Model model) {
return this.STOCKADMINFRONTENDTEMPLATE; return this.STOCKADMINFRONTENDTEMPLATE;
} }
\end{lstlisting} \end{lstlisting}
Weiter wurde eine HTML-Datei \texttt{admin.html} zu den statischen Webseiteninhalten des Monolithen hinzugefügt. Diese leitet, wie nachfolgend dargestellt, durch einen Meta-Eintrag direkt auf die Webseite des Microservice Warenwirtschaft weiter. Für den Fall, dass die Umleitung durch den verwendeten Browser nicht unterstützt wird, wurde zudem ein Link auf die Webseite des Microservice integriert. Weiter wurde eine HTML-Datei \texttt{stockadmin.html} zu den statischen Webseiteninhalten des Monolithen hinzugefügt. Diese leitet, wie nachfolgend dargestellt, durch einen Meta-Eintrag direkt auf die Webseite des Microservice Warenwirtschaft weiter. Für den Fall, dass die Umleitung durch den verwendeten Browser nicht unterstützt wird, wurde zudem ein Link auf die Webseite des Microservice integriert.
\begin{lstlisting}[caption=Datenabfrage von der Bestellung (Waren wurden bestellt), language=HTML] \begin{lstlisting}[caption=Datenabfrage von der Bestellung (Waren wurden bestellt), language=HTML]
<head th:replace="fragments/skeleton :: head"> <head th:replace="fragments/skeleton :: head">

View File

@ -29,15 +29,6 @@
\input{./chapter/Anforderungen} \newpage \input{./chapter/Anforderungen} \newpage
\input{./chapter/Struktur} \newpage \input{./chapter/Struktur} \newpage
\input{./chapter/Implementierungsregeln}\newpage \input{./chapter/Implementierungsregeln}\newpage
\input{./chapter/Testfalle} \newpage
\clearpage
\appendix
\phantomsection
\addcontentsline{toc}{section}{\appendixname}
\input{./chapter/GettingStarted} \newpage \input{./chapter/GettingStarted} \newpage
\input{./chapter/Handout} \newpage
\input{./chapter/Testprotokoll}
\end{document} \end{document}

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View File

@ -37,7 +37,7 @@ func listGoods(w http.ResponseWriter, r *http.Request) {
log.Info("done") log.Info("done")
} }
// Function that counts als available goods for one product // Function that counts all available goods for one product
func getGoodAvailablityCount(w http.ResponseWriter, r *http.Request) (int, *logrus.Entry) { func getGoodAvailablityCount(w http.ResponseWriter, r *http.Request) (int, *logrus.Entry) {
log := logger.HTTP(r) log := logger.HTTP(r)
id, err := strconv.ParseInt(pat.Param(r, "productid"), 10, 64) id, err := strconv.ParseInt(pat.Param(r, "productid"), 10, 64)

View File

@ -9,9 +9,9 @@ import (
"text/template" "text/template"
) )
// Path to the svg image template, that shows the availablity of a given good // Path to the svg image template, that shows the availablity or freshness of a given good
// with a traffic light food labeling system // with a traffic light food labeling system
var GoodAvailablityTemplate string var GoodAvailabilityTemplate string
var GoodFreshnessTemplate string var GoodFreshnessTemplate string
// Function to calculate a percent value from a given value and an maximum value // Function to calculate a percent value from a given value and an maximum value
@ -24,7 +24,7 @@ func tempProcessRadius(value, max, radius int) float64 {
return (1 - float64(value)/float64(max)) * float64(radius) * 2 * 3.14 return (1 - float64(value)/float64(max)) * float64(radius) * 2 * 3.14
} }
// Function to get the SVG, that shows availybility with a traffic light food labeling system for a given good // Function to get the SVG, that shows the availybility with a traffic light food labeling system for a given good
func getGoodAvailablitySVG(w http.ResponseWriter, count int) { func getGoodAvailablitySVG(w http.ResponseWriter, count int) {
t := template.New("some") t := template.New("some")
@ -32,7 +32,7 @@ func getGoodAvailablitySVG(w http.ResponseWriter, count int) {
"process_radius": tempProcessRadius, "process_radius": tempProcessRadius,
}) })
buf := bytes.NewBuffer(nil) buf := bytes.NewBuffer(nil)
f, _ := os.Open(GoodAvailablityTemplate) // Error handling elided for brevity. f, _ := os.Open(GoodAvailabilityTemplate) // Error handling elided for brevity.
io.Copy(buf, f) // Error handling elided for brevity. io.Copy(buf, f) // Error handling elided for brevity.
f.Close() f.Close()
@ -44,7 +44,7 @@ func getGoodAvailablitySVG(w http.ResponseWriter, count int) {
} }
// Function to get the SVG, that shows freshness with a traffic light food labeling system for a given good // Function to get the SVG, that shows the freshness with a traffic light food labeling system for a given good
func getGoodFreshnessSVG(w http.ResponseWriter, fresh bool) { func getGoodFreshnessSVG(w http.ResponseWriter, fresh bool) {
t := template.New("some") t := template.New("some")

View File

@ -10,7 +10,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
// Function to the the permission and it's error handling // Function to test the permission and it's error handling
func TestPermission(t *testing.T) { func TestPermission(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)

View File

@ -8,13 +8,14 @@ import (
logger "github.com/Sirupsen/logrus" logger "github.com/Sirupsen/logrus"
) )
// Crrrent logger with it's configuration // Current logger with it's configuration
var Log *logger.Logger var Log *logger.Logger
// Function to initiate a new logger // Function to initiate a new logger
func init() { func init() {
Log = logger.New() Log = logger.New()
log.SetOutput(Log.Writer()) // Enable fallback if core logger // Enable fallback, if core logger
log.SetOutput(Log.Writer())
} }
// Function to add the information of a http request to the log // Function to add the information of a http request to the log

View File

@ -10,7 +10,7 @@ type Worker struct {
quit chan struct{} quit chan struct{}
} }
// Function to reate a new Worker with a timestamp, run, every and it's function // Function to create a new Worker with a timestamp, run, every and it's function
func NewWorker(every time.Duration, f func()) (w *Worker) { func NewWorker(every time.Duration, f func()) (w *Worker) {
w = &Worker{ w = &Worker{
every: every, every: every,
@ -21,7 +21,7 @@ func NewWorker(every time.Duration, f func()) (w *Worker) {
} }
// Function to start the Worker // Function to start the Worker
// (please us it as a goroutine with go w.Start()) // (please us it as a go routine with go w.Start())
func (w *Worker) Start() { func (w *Worker) Start() {
ticker := time.NewTicker(w.every) ticker := time.NewTicker(w.every)
for { for {

View File

@ -10,7 +10,7 @@ import (
"github.com/genofire/hs_master-kss-monolith/lib/log" "github.com/genofire/hs_master-kss-monolith/lib/log"
) )
// Config file for this daemon (mor information at the config_example.conf in this git repository) // Config file for this daemon (more information at the config_example.conf in this git repository)
type Config struct { type Config struct {
// address under which the api and static content of the webserver runs // address under which the api and static content of the webserver runs
WebserverBind string `toml:"webserver_bind"` WebserverBind string `toml:"webserver_bind"`
@ -22,8 +22,9 @@ type Config struct {
GoodRelease GoodReleaseConfig `toml:"good_release"` GoodRelease GoodReleaseConfig `toml:"good_release"`
CacheClean CacheWorkerConfig `toml:"cache_clean"` CacheClean CacheWorkerConfig `toml:"cache_clean"`
// path to the svg image templaes to show availablity of a given good with a traffic light food labeling system // path to the svg image templates to show the availablity and freshness
GoodAvailablityTemplate string `toml:"good_availablity_template"` // of a given good with a traffic light food labeling system
GoodAvailabilityTemplate string `toml:"good_availablity_template"`
GoodFreshnessTemplate string `toml:"good_freshness_template"` GoodFreshnessTemplate string `toml:"good_freshness_template"`
// URLs to other microservices that this services uses // URLs to other microservices that this services uses
@ -45,18 +46,17 @@ type CacheWorkerConfig struct {
type GoodReleaseConfig struct { type GoodReleaseConfig struct {
// Run worker every Duration // Run worker every Duration
Every Duration `toml:"every"` Every Duration `toml:"every"`
// unlock which is not used since Duration // Unlock those which are not used since Duration
After Duration `toml:"after"` After Duration `toml:"after"`
} }
// Function that reads a config model from a given path of a yml file // Function that reads a config model from a given path of a .yml file
func ReadConfigFile(path string) *Config { func ReadConfigFile(path string) *Config {
config := &Config{} config := &Config{}
file, err := ioutil.ReadFile(path) file, err := ioutil.ReadFile(path)
if err != nil { if err != nil {
log.Log.Panic(err) log.Log.Panic(err)
} }
if err := toml.Unmarshal(file, config); err != nil { if err := toml.Unmarshal(file, config); err != nil {
log.Log.Panic(err) log.Log.Panic(err)
} }

View File

@ -27,12 +27,12 @@ type Good struct {
Sended bool `json:"-"` Sended bool `json:"-"`
} }
// Function to enerate a database and select locked goods with a filter // Function to generate a database and select locked goods with a filter
func (g *Good) FilterAvailable(db *gorm.DB) *gorm.DB { func (g *Good) FilterAvailable(db *gorm.DB) *gorm.DB {
return db.Model(g).Where("locked_secret == '' OR locked_secret is NULL") return db.Model(g).Where("locked_secret == '' OR locked_secret is NULL")
} }
// Function to lock a good, so that it cannot be locked or bought by other users // Function to lock a good, so that it cannot be locked (bought) by other users
func (g *Good) Lock(secret string) { func (g *Good) Lock(secret string) {
now := time.Now() now := time.Now()
g.LockedSecret = secret g.LockedSecret = secret

View File

@ -23,7 +23,7 @@ const (
PermissionCreateGood = 1 PermissionCreateGood = 1
// permission to delete goods from the stock // permission to delete goods from the stock
// e.g. if a good become rancid and has to be removed // e.g. if a good becomes fouled and has to be removed
PermissionDeleteGood = 2 PermissionDeleteGood = 2
) )
@ -35,7 +35,6 @@ type permissionMicroServiceCache struct {
sync.Mutex sync.Mutex
} }
// Function to check, if a user has a permission // Function to check, if a user has a permission
func (c *permissionMicroServiceCache) HasPermission(p Permission) (bool, error) { func (c *permissionMicroServiceCache) HasPermission(p Permission) (bool, error) {
c.LastCheck = time.Now() c.LastCheck = time.Now()
@ -68,6 +67,7 @@ func (c *permissionMicroServiceCache) HasPermission(p Permission) (bool, error)
// Cache for permissions // Cache for permissions
var permissionCache map[string]*permissionMicroServiceCache var permissionCache map[string]*permissionMicroServiceCache
var permissionMutex sync.Mutex var permissionMutex sync.Mutex
// Function to initialize the permission cache // Function to initialize the permission cache
func init() { func init() {
permissionCache = make(map[string]*permissionMicroServiceCache) permissionCache = make(map[string]*permissionMicroServiceCache)

View File

@ -13,7 +13,7 @@ import (
// URL to the microservice which manages the products (product catalogue) // URL to the microservice which manages the products (product catalogue)
var ProductURL string var ProductURL string
// Struct tht holds the information on the microservice cache // Struct that holds the information on the microservice cache
type boolMicroServiceCache struct { type boolMicroServiceCache struct {
LastCheck time.Time LastCheck time.Time
Value bool Value bool

View File

@ -1,4 +1,4 @@
// Package that contains a lib to easily create everything for running a virtual api // Package that contains a lib to easily create everything for running a virtual api and test the microservice
package test package test
// Import an easy manager to test the REST-API // Import an easy manager to test the REST-API
@ -55,7 +55,7 @@ type Request struct {
router *goji.Mux router *goji.Mux
} }
// Function tot create a NewSession with the easy manager // Function to create a NewSession with the easy manager
func NewSession(router *goji.Mux) *Request { func NewSession(router *goji.Mux) *Request {
return &Request{router: router} return &Request{router: router}
} }

View File

@ -7,8 +7,8 @@
<form class="ui form segment" ng-submit="submit()" ng-class="{'top attached':msg.type}"> <form class="ui form segment" ng-submit="submit()" ng-class="{'top attached':msg.type}">
<div class="field"> <div class="field">
<label>Fouled at</label> <label>Expiration Date</label>
<input type="date" name="fouled_at" placeholder="Fouled at date" ng-model="obj.fouled_at"> <input type="date" name="fouled_at" placeholder="Fouled at date (e.g. 2017-06-30)" ng-model="obj.fouled_at">
</div> </div>
<div class="field"> <div class="field">
<label>Position</label> <label>Position</label>
@ -16,7 +16,7 @@
</div> </div>
<div class="field"> <div class="field">
<label>Comment</label> <label>Comment</label>
<input type="text" name="comment" placeholder="Comment to this good", ng-model="obj.comment"> <input type="text" name="comment" placeholder="Comment for this good", ng-model="obj.comment">
</div> </div>
<div class="field"> <div class="field">
<label>Count</label> <label>Count</label>

View File

@ -23,7 +23,6 @@
<th>#</th> <th>#</th>
<th>Location</th> <th>Location</th>
<th>Comment</th> <th>Comment</th>
<th>Time of Delivery</th>
<th>Status of Freshness</th> <th>Status of Freshness</th>
<th></th> <th></th>
</tr> </tr>
@ -33,7 +32,6 @@
<td>{{item.id}}</td> <td>{{item.id}}</td>
<td>{{item.position}}</td> <td>{{item.position}}</td>
<td>{{item.comment}}</td> <td>{{item.comment}}</td>
<td></td>
<td valign="middle"><img class="icon" ng-src="{{'/api/good/freshness/'+item.id| reloadSrc}}"/></td> <td valign="middle"><img class="icon" ng-src="{{'/api/good/freshness/'+item.id| reloadSrc}}"/></td>
<td><i class="trash icon" ng-click="delete(item.id)"></i></td> <td><i class="trash icon" ng-click="delete(item.id)"></i></td>
</tr> </tr>

View File

@ -23,9 +23,9 @@ angular.module('microStock')
} }
last.then(function(){ last.then(function(){
$scope.obj = {}; $scope.obj = {};
$scope.msg = {type:'success',text:'There was '+count+' goods saved from '+$scope.product.title+'.'}; $scope.msg = {type:'success',text:'Saved '+count+' good(s) from product '+$scope.product.title+'.'};
},function(){ },function(){
$scope.msg = {type:'error',text:'There was '+count+' goods saved from '+$scope.product.title+'.'}; $scope.msg = {type:'error',text:'Saved '+count+' good(s) from product '+$scope.product.title+'.'};
}) })
}; };
}]); }]);