From 7b4abb17eb66003f2c1e7e721cc5a1178d09e358 Mon Sep 17 00:00:00 2001
From: Volker Schukai <volker.schukai@schukai.com>
Date: Wed, 22 Jun 2022 17:14:07 +0200
Subject: [PATCH] chore: commit save point

---
 README.md                                     | 137 ++++++++++--------
 application/source/commandline.go             |   3 +-
 application/source/gitlab.go                  |  52 ++++---
 application/source/issues.go                  |  18 ++-
 development/examples/example1/req1/1/test1.md |  37 +++--
 5 files changed, 149 insertions(+), 98 deletions(-)

diff --git a/README.md b/README.md
index c1d01d9..d099423 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,6 @@ chmod u+x reqman
 The date format for the output can be specified with the parameter `--date-format`.
 The format can be looked up in the first column of the table below.
 
-
 | Layout           | Java notation | C notation | Notes    |
 | ------------------ | --------------- | ------------ | ---------- |
 | 2006-01-02       | yyyy-MM-dd    | %F         | ISO 8601 |
@@ -93,7 +92,6 @@ reqman tasks print --path=examples/
 
 The output is then of the type:
 
-
 | AUFGABE                  | BIS | ERLEDIGT | DATEI              |
 | -------------------------- | ----- | ---------- | -------------------- |
 | das ist das erste todo 1 | —  | —       | ./req1/001-1425.md |
@@ -106,7 +104,6 @@ reqman requirements print --path examples/ --column ID --column Title
 
 The output is then of the type:
 
-
 | ID | TITEL          |
 | ---- | ---------------- |
 | A1 | My requirement |
@@ -127,7 +124,6 @@ reqman requirements print --path=example/ --column=ID --column=
 
 The output is then of the type:
 
-
 | ID                 | GRUPPE | NAME                     | TYP  | BESCHREIBUNG | BIS        | ERLEDIGT | AM         | VON | DATEI                |
 | -------------------- | -------- | -------------------------- | ------ | -------------- | ------------ | ---------- | ------------ | ----- | ---------------------- |
 | 23423-Beistellung1 | A1     | Beistellung eines Bildes | Bild | Großes Logo | 2022-12-12 | ✓       | 2022-12-01 | Me  | ./req1/002/002-01.md |
@@ -159,37 +155,36 @@ requirements report --grouped-by ID
 
 This command produces two tables:
 
-
 | Anforderung | Gesch | TIME SPENT |
-| ------------- | ------- | ------------ |
-| ID1         | 40    | 1          |
-| ID2         | 40    | 1          |
-| ID3         | 40    | 1          |
-| ID4         | 40    | 1          |
-| ID5         | 40    | 2          |
-| ID6         | 40    | 2          |
-| ID7         | 40    | 2          |
-| ID8         | 40    | 2          |
-| ID9         | 40    | 2          |
-| ID10        | 40    | 2          |
-| ID11        | 40    | 3          |
-| ID12        | 40    | 3          |
-| ID13        | 40    | 3          |
-| ID14        | 40    | 3          |
-| ID15        | 40    | 3          |
-| ID16        | 40    | 3          |
-| ID17        | 40    | 3          |
-| ID18        | 40    | 4          |
-| ID19        | 40    | 4          |
-| ID20        | 40    | 4          |
-| ID21        | 40    | 4          |
-| ID22        | 40    | 5          |
-| ID23        | 40    | 5          |
-| ID24        | 40    | 6          |
-| ID25        | 40    | 6          |
-
-1) Groups all estimates and adds up the estimate and the booked times. This allows you to see how much time you have spent.
-
+| ------------- | ------- | --------- |
+| ID1         | 40    | 1         |
+| ID2         | 40    | 1         |
+| ID3         | 40    | 1         |
+| ID4         | 40    | 1         |
+| ID5         | 40    | 2         |
+| ID6         | 40    | 2         |
+| ID7         | 40    | 2         |
+| ID8         | 40    | 2         |
+| ID9         | 40    | 2         |
+| ID10        | 40    | 2         |
+| ID11        | 40    | 3         |
+| ID12        | 40    | 3         |
+| ID13        | 40    | 3         |
+| ID14        | 40    | 3         |
+| ID15        | 40    | 3         |
+| ID16        | 40    | 3         |
+| ID17        | 40    | 3         |
+| ID18        | 40    | 4         |
+| ID19        | 40    | 4         |
+| ID20        | 40    | 4         |
+| ID21        | 40    | 4         |
+| ID22        | 40    | 5         |
+| ID23        | 40    | 5         |
+| ID24        | 40    | 6         |
+| ID25        | 40    | 6         |
+
+1) Groups all estimates and adds up the estimate and the booked times. This allows you to see how much time you have
+   spent.
 
 | KEY | PLANNED VALUE | TIME SPENT | RATIO |
 | :---- | --------------- | ------------ | ------- |
@@ -197,7 +192,6 @@ This command produces two tables:
 
 2) This table shows for each plan value the hours needed, the mean, the variance and the standard deviation.
 
-
 | PLAN VALUE | TIME SPENT | MEAN | VARIANCE | STANDARD DEVIATION |
 | ------------ | ------------ | ------ | ---------- | -------------------- |
 | 40         | 75h0m0s    | 3.00 | 2.08     | 1.44               |
@@ -211,6 +205,30 @@ The smaller the standard deviation, the better.
 So what decision can we derive? If a new requirement is estimated at
 40, the hours offered should be between 3 and 5.
 
+### Sync with Gitlab
+
+You can synchronize the issues of a document with a Gitlab server. 
+
+```bash 
+reqman gitlab sync --path=example/ 
+```
+
+If no ID is defined in the document, an issue is created, 
+
+```yaml
+issues:
+   - gitlab:
+        title: "Issue 1"
+```
+
+otherwise the data is updated.
+
+```yaml
+issues:
+   - gitlab:
+        id: 1
+```
+
 ## Structure
 
 ### The Text
@@ -223,42 +241,43 @@ The data from the YAML block can be integrated in the text via [placeholders](ht
 For example, the following statement can be used to include the items within the document.
 
 ```golang
-{{ if .Items }}
+{{ if.Items }}
 **Items**
 
 | ID                 | Name         |        Delivery until |                              Provided on |
 |--------------------|--------------|----------------------:|-----------------------------------------:|
-{{ range .Items 
- }}| {{ .ID }} | {{ .Name }} | {{ .DeliveryUntil.Format "02.Jan" }} | {{ .ProvidedOn.Format "02.Jan"  }} | 
+{{ range.Items
+}}| {{ .ID }} | {{ .Name }} | {{ .DeliveryUntil.Format "02.Jan" }} | {{ .ProvidedOn.Format "02.Jan"  }} |
 {{ end }}
 
 {{ end }}
 ```
 
-If the PDF function is used, LaTeX commands can also be included in the text. For example, the LaTex instruction `/newline' leads to a line break.
+If the PDF function is used, LaTeX commands can also be included in the text. For example, the LaTex instruction `
+/newline' leads to a line break.
 
 ## YAML
 
-
-|                                   | `>`   | `|`    |     | `"`  | `'` | `>-`  | `>+`   | `|-`   | `|+`   |
-| --------------------------------- | ----- | ------ | --- | ---- | --- | ----- | ------ | ------ | ------ |
-| **Spaces/newlines converted as:** |       |        |     |      |     |       |        |        |        |
-| Trailing space →                  | \_    | \_     |     |      |     | \_    | \_     | \_     | \_     |
-| Leading space →                   | \\n\_ | \\n\_  |     |      |     | \\n\_ | \\n\_  | \\n\_  | \\n\_  |
-| Single newline →                  | \_    | \\n    | \_  | \_   | \_  | \_    | \_     | \\n    | \\n    |
-| Double newline →                  | \\n   | \\n\\n | \\n | \\n  | \\n | \\n   | \\n    | \\n\\n | \\n\\n |
-| Final newline →                   | \\n   | \\n    |     |      |     |       | \\n    |        | \\n    |
-| Final double newline →            |       |        |     |      |     |       | \\n\\n |        | \\n\\n |
-| **How to create a literal:**      |       |        |     |      |     |       |        |        |        |
-| Single quote                      | '     | '      | '   | '    | ''  | '     | '      | '      | '      |
-| Double quote                      | "     | "      | "   | \\"  | "   | "     | "      | "      | "      |
-| Backslash                         | \\    | \\     | \\  | \\\\ | \\  | \\    | \\     | \\     | \\     |
-| **Other features**                |       |        |     |      |     |       |        |        |        |
-| In-line newlines with `\n`        | 🚫    | 🚫     | 🚫  | ✅   | 🚫  | 🚫    | 🚫     | 🚫     | 🚫    |
-| Spaceless newlines with `\`       | 🚫    | 🚫     | 🚫  | ✅   | 🚫  | 🚫    | 🚫     | 🚫     | 🚫    |
-| `#` or `:` in value               | ✅    | ✅     | 🚫  | ✅   | ✅  | ✅    | ✅     | ✅     | ✅    |
-| Can start on same line as key     | 🚫    | 🚫     | ✅  | ✅   | ✅  | 🚫    | 🚫     | 🚫     | 🚫    |
-
+|                                   | `>`     | `│`    |     | `"`  | `'` | `>-`  | `>+`   | `│-`   | `│+`   |
+|-----------------------------------|---------|--------|-----|------|-----|-------|--------|--------|--------|
+| **Spaces/newlines converted as:** |         |        |     |      |     |       |        |        |        |
+| Trailing space →                  | \_      | \_     |     |      |     | \_    | \_     | \_     | \_     |
+| Leading space →                   | \\n\_   | \\n\_  |     |      |     | \\n\_ | \\n\_  | \\n\_  | \\n\_  |
+| Single newline →                  | \_      | \\n    | \_  | \_   | \_  | \_    | \_     | \\n    | \\n    |
+| Double newline →                  | \\n     | \\n\\n | \\n | \\n  | \\n | \\n   | \\n    | \\n\\n | \\n\\n |
+| Final newline →                   | \\n     | \\n    |     |      |     |       | \\n    |        | \\n    |
+| Final double newline →            |         |        |     |      |     |       | \\n\\n |        | \\n\\n |
+| **How to create a literal:**      |         |        |     |      |     |       |        |        |        |
+| Single quote                      | '       | '      | '   | '    | ''  | '     | '      | '      | '      |
+| Double quote                      | "       | "      | "   | \\"  | "   | "     | "      | "      | "      |
+| Backslash                         | \\      | \\     | \\  | \\\\ | \\  | \\    | \\     | \\     | \\     |
+| **Other features**                |         |        |     |      |     |       |        |        |        |
+| In-line newlines with `\n`        | 🚫      | 🚫     | 🚫  | ✅    | 🚫  | 🚫    | 🚫     | 🚫     | 🚫     |
+| Spaceless newlines with `\`       | 🚫      | 🚫     | 🚫  | ✅    | 🚫  | 🚫    | 🚫     | 🚫     | 🚫     |
+| `#` or `:` in value               | ✅       | ✅      | 🚫  | ✅    | ✅   | ✅     | ✅      | ✅      | ✅      |
+| Can start on same line as key     | 🚫      | 🚫     | ✅   | ✅    | ✅   | 🚫    | 🚫     | 🚫     | 🚫     |
+
+`│ -> |`
 
 ## Latex
 
diff --git a/application/source/commandline.go b/application/source/commandline.go
index 3b1bc54..35b5366 100644
--- a/application/source/commandline.go
+++ b/application/source/commandline.go
@@ -1,7 +1,6 @@
 package main
 
 import (
-	"fmt"
 	"os"
 	"path"
 	"path/filepath"
@@ -42,7 +41,7 @@ func executeCommand() {
 	switch activeCommand.Name {
 
 	case "version":
-		fmt.Println("Version " + version + " (" + build + ")")
+		printInfo("Version " + version + " (" + build + ")")
 	case "changelog":
 		subcommand := activeCommand.Active
 		switch subcommand.Name {
diff --git a/application/source/gitlab.go b/application/source/gitlab.go
index 1f6ff04..7ca2a44 100644
--- a/application/source/gitlab.go
+++ b/application/source/gitlab.go
@@ -2,7 +2,6 @@ package main
 
 import (
 	"errors"
-	"fmt"
 	"net/url"
 
 	"path"
@@ -250,33 +249,52 @@ func recursiveMerge(from, into *yaml.Node) error {
 
 func syncIssuesWithGitlab(pageData map[string]*requirement) {
 
+	err := enrichIssuesWithGitlab(pageData)
+	if err != nil {
+		printError(err.Error())
+		printErrorAndExit(2, "Failed to enrich issues with gitlab")
+	}
+
 	for _, pageData := range pageData {
 		for k, info := range pageData.Issues {
-			if info.GitlabRemote != nil {
-				continue
-			}
-			if info.GitlabIntern == nil {
-				continue
-			}
+
 			if info.GitlabIntern.ID == 0 {
 				issue, err := createIssue(info)
 				if err != nil {
 					printErrorAndExit(2, "Failed to create issue %s", err.Error())
 				}
+
 				pageData.Issues[k].GitlabRemote = issue
 				pageData.Issues[k].GitlabIntern = new(GitlabInternalIssueStruct)
 				pageData.Issues[k].GitlabIntern.ID = issue.IID
+				pageData.Issues[k].GitlabIntern.URL = issue.WebURL
+				pageData.Issues[k].GitlabIntern.Title = issue.Title
+				pageData.Issues[k].GitlabIntern.Description = issue.Description
+				pageData.Issues[k].GitlabIntern.Labels = issue.Labels
+				pageData.Issues[k].GitlabIntern.Status = issue.State
 
-				var change yaml.Node
-				bytes, err := yaml.Marshal(&pageData)
-				if err != nil {
-					fmt.Println(err)
-				}
-				yaml.Unmarshal(bytes, &change)
-				recursiveRemove(pageData.OriginNode)
-				recursiveMerge(&change, pageData.OriginNode)
+			} else {
+
+				pageData.Issues[k].GitlabIntern = new(GitlabInternalIssueStruct)
+				pageData.Issues[k].GitlabIntern.ID = info.GitlabRemote.IID
+				pageData.Issues[k].GitlabIntern.URL = info.GitlabRemote.WebURL
+				pageData.Issues[k].GitlabIntern.Title = info.GitlabRemote.Title
+				pageData.Issues[k].GitlabIntern.Description = info.GitlabRemote.Description
+				pageData.Issues[k].GitlabIntern.Labels = info.GitlabRemote.Labels
+				pageData.Issues[k].GitlabIntern.Status = info.GitlabRemote.State
 			}
 
+			var change yaml.Node
+			bytes, err := yaml.Marshal(&pageData)
+			if err != nil {
+				printError("Failed to marshal page data %s", err.Error())
+				continue
+			}
+
+			yaml.Unmarshal(bytes, &change)
+			recursiveRemove(pageData.OriginNode)
+			recursiveMerge(&change, pageData.OriginNode)
+
 		}
 	}
 }
@@ -309,8 +327,8 @@ func createIssue(issue Issue) (*gitlab.Issue, error) {
 		return gitlabIssue, err
 	}
 
-	printInfo("Created issue %s (%s)", gitlabIssue.Title, gitlabIssue.ID)
-	printInfo("  %s", gitlabIssue.WebURL)
+	printInfo("Created issue %v (ID %v)", gitlabIssue.Title, gitlabIssue.IID)
+	printInfo("\t%s", gitlabIssue.WebURL)
 
 	return gitlabIssue, nil
 }
diff --git a/application/source/issues.go b/application/source/issues.go
index 0cb9f5b..372baa6 100644
--- a/application/source/issues.go
+++ b/application/source/issues.go
@@ -11,16 +11,18 @@ import (
 )
 
 type GitlabInternalIssueStruct struct {
-	ID          int    `yaml:"ID"`
-	Title       string `yaml:"Title,omitempty"`
-	Description string `yaml:"Description,omitempty"`
-	//Priority    string   `yaml:"Priority,omitempty"`
-	Status string `yaml:"Status,omitempty"`
-	//Assignee    string   `yaml:"Assignee,omitempty"`
-	//Milestone   string   `yaml:"Milestone,omitempty"`
-	Labels []string `yaml:"Labels,omitempty"`
+	ID          int      `yaml:"ID"`
+	Title       string   `yaml:"Title,omitempty"`
+	Description string   `yaml:"Description,omitempty"`
+	Status      string   `yaml:"Status,omitempty"`
+	Labels      []string `yaml:"Labels,omitempty"`
+	URL         string   `yaml:"URL,omitempty"`
 }
 
+//Priority    string   `yaml:"Priority,omitempty"`
+//Assignee    string   `yaml:"Assignee,omitempty"`
+//Milestone   string   `yaml:"Milestone,omitempty"`
+
 type Issue struct {
 	GitlabIntern *GitlabInternalIssueStruct `yaml:"Gitlab"`
 	GitlabRemote *gitlab.Issue              `yaml:"-"`
diff --git a/development/examples/example1/req1/1/test1.md b/development/examples/example1/req1/1/test1.md
index a0f6a3b..a28b2fd 100644
--- a/development/examples/example1/req1/1/test1.md
+++ b/development/examples/example1/req1/1/test1.md
@@ -39,18 +39,6 @@ Time Spent: 2h
 Source: null
 Created: 2022-05-03
 Last Update: null
-Issues:
-    - Gitlab:
-        ID: 1
-    - Gitlab:
-        Title: Test 1
-        Status: offen
-        Description: |
-            Lorem Ipsum Lila Rot 
-            Blau1 Lila Rot Blau2
-        Labels:
-            - a
-            - b
 # the individual items as a list
 Items:
     - ID: null
@@ -103,7 +91,32 @@ Privacy:
       # Where possible, a general description of the technical and organisational 
       # security measures referred to in Article 32(1).
       TOM: null
+Issues:
+    - Gitlab:
+        ID: 1
+        Title: demo
+        Status: closed
+        Labels:
+            - bug
+            - enhancement
+            - feature
+        URL: https://gitlab.schukai.com/oss/utilities/requirements-manager/-/issues/1
+    - Gitlab:
+        ID: 47
+        Title: Test 15
+        Description: |-
+            Lorem Ipsum Lila Rot
 
+            Blau1 Lila Rot Blau2
+
+            ```
+            a=4
+            ```
+        Status: closed
+        Labels:
+            - bug
+            - bugfix
+        URL: https://gitlab.schukai.com/oss/utilities/requirements-manager/-/issues/47
 
 ...
 ### {{ .Title }} - {{ .ID }}
-- 
GitLab