main.initApp   F
last analyzed

Complexity

Conditions 18

Size

Total Lines 98
Code Lines 63

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 18
eloc 63
dl 0
loc 98
rs 1.2
c 0
b 0
f 0
nop 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like main.initApp often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
package main
2
3
import (
4
	"encoding/json"
5
	"fmt"
6
	"io"
7
	"io/ioutil"
8
	"log"
9
	"net/http"
10
	"os"
11
	"time"
12
13
	"flag"
14
	"github.com/getlantern/systray"
15
	"github.com/popstas/go-toggl"
16
	"github.com/viasite/planfix-toggl-server/app/client"
17
	"github.com/viasite/planfix-toggl-server/app/config"
18
	"github.com/viasite/planfix-toggl-server/app/icon"
19
	"github.com/viasite/planfix-toggl-server/app/rest"
20
	"github.com/viasite/planfix-toggl-server/app/util"
21
	"runtime"
22
)
23
24
var version string
25
var trayMenu map[string] *systray.MenuItem
26
27
func getLogger(cfg config.Config) *log.Logger {
28
	// logging
29
	logger := log.New(os.Stderr, "[planfix-toggl] ", log.LstdFlags)
30
	if cfg.Debug {
31
		logger.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds | log.Lshortfile)
32
	} else {
33
		toggl.DisableLog()
34
	}
35
	if cfg.LogFile != "" {
36
		f, err := os.OpenFile(cfg.LogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
37
		if err != nil {
38
			logger.Fatalf("[ERROR] Failed to open log file: %s", cfg.LogFile)
39
		}
40
		//defer f.Close()
41
		// file should be first, when -ldflags -H=windowsgui build, Stdout absent and block log output
42
		mw := io.MultiWriter(f, os.Stdout)
43
		logger.SetOutput(mw)
44
	}
45
	return logger
46
}
47
48
func parseFlags(cfg *config.Config) {
49
	dryRun := flag.Bool("dry-run", false, "Don't actually change data")
50
	if runtime.GOOS == "windows" {
51
		// Allow user to hide the console window
52
		flag.BoolVar(&cfg.NoConsole, "no-console", false, "Hide console window")
53
	}
54
	flag.Parse()
55
56
	if *dryRun {
57
		cfg.DryRun = true
58
	}
59
}
60
61
func connectServices(cfg *config.Config, logger *log.Logger, togglClient *client.TogglClient) (err error) {
62
	// toggl
63
	logger.Println("[INFO] подключение к Toggl...")
64
	account, err := togglClient.GetTogglUser()
65
	cfg.TogglUserID = account.Data.ID
66
	if err != nil {
67
		return err
68
	}
69
70
	ok, err := togglClient.IsWorkspaceExists(cfg.TogglWorkspaceID)
71
	if err != nil {
72
		return err
73
	}
74
	if !ok {
75
		return fmt.Errorf("Toggl workspace ID %d не найден", cfg.TogglWorkspaceID)
76
	}
77
78
	// planfix
79
	if cfg.PlanfixUserName != "" && cfg.PlanfixUserPassword != "" {
80
		logger.Println("[INFO] подключение к Планфиксу...")
81
		user, err := togglClient.GetPlanfixUser()
82
		cfg.PlanfixUserID = user.ID
83
		if err != nil {
84
			return err
85
		}
86
87
		logger.Println("[INFO] получение данных аналитики Планфикса...")
88
		_, err = togglClient.GetAnaliticDataCached(
89
			cfg.PlanfixAnaliticName,
90
			cfg.PlanfixAnaliticTypeName,
91
			cfg.PlanfixAnaliticTypeValue,
92
			cfg.PlanfixAnaliticCountName,
93
			cfg.PlanfixAnaliticCommentName,
94
			cfg.PlanfixAnaliticDateName,
95
			cfg.PlanfixAnaliticUsersName,
96
		)
97
		if err != nil {
98
			return fmt.Errorf("Поля аналитики указаны неправильно: %s", err.Error())
99
		}
100
	}
101
	return nil
102
}
103
104
func initApp() {
105
	fmt.Printf("planfix-toggl %s\n", version)
106
	cfg := config.GetConfig()
107
108
	parseFlags(&cfg)
109
110
	logger := getLogger(cfg)
111
112
	errors, isValid := cfg.Validate()
113
	if !isValid {
114
		for _, e := range errors {
115
			log.Println(e)
116
		}
117
	}
118
119
	if cfg.NoConsole {
120
		util.HideConsole()
121
	}
122
123
	if cfg.CheckNewVersion {
124
		checkNewVersion()
125
	}
126
127
	togglClient := client.TogglClient{
128
		Config:  &cfg,
129
		Logger:  logger,
130
		SentLog: make(map[string]int),
131
		Opts:    map[string]string{"LastSent": ""},
132
	}
133
	togglClient.ReloadConfig()
134
135
	// get planfix and toggl user IDs, for early API check
136
	err := connectServices(&cfg, logger, &togglClient)
137
	if err != nil {
138
		isValid = false
139
		logger.Printf("[ERROR] %s", err.Error())
140
		util.Notify(err.Error())
141
	}
142
143
	trayMenu["web"].Enable()
144
	trayMenu["log"].Enable()
145
146
	if isValid {
147
		trayMenu["send"].Enable()
148
		togglClient.Run()
149
	} else {
150
		util.OpenBrowser(fmt.Sprintf("https://localhost:%d/#settings", cfg.PortSSL))
151
	}
152
153
	// update last sent on menuitem
154
	go func() {
155
		for {
156
			if togglClient.Opts["LastSent"] != "" {
157
				t := togglClient.Opts["LastSent"]
158
				trayMenu["send"].SetTitle(fmt.Sprintf("Sync (last at %s)", t))
159
			}
160
			time.Sleep(time.Duration(60 * time.Second))
161
		}
162
	}()
163
164
	go func() {
165
		// tray menu actions
166
		for {
167
			select {
168
			case <-trayMenu["send"].ClickedCh:
169
				err := togglClient.SendToPlanfix()
170
				t := togglClient.Opts["LastSent"]
171
				trayMenu["send"].SetTitle(fmt.Sprintf("Sync (last at %s)", t))
172
				if err != nil {
173
					logger.Println(err)
174
				}
175
176
			case <-trayMenu["web"].ClickedCh:
177
				cfg := config.GetConfig()
178
				util.OpenBrowser(fmt.Sprintf("https://localhost:%d", cfg.PortSSL))
179
180
			case <-trayMenu["current"].ClickedCh:
181
				cfg := config.GetConfig()
182
				util.OpenBrowser(fmt.Sprintf("https://localhost:%d/#current-go", cfg.PortSSL))
183
184
			case <-trayMenu["log"].ClickedCh:
185
				cfg := config.GetConfig()
186
				systray.ShowAppWindow(fmt.Sprintf("https://localhost:%d/log", cfg.PortSSL))
187
188
			case <-trayMenu["quit"].ClickedCh:
189
				onExit()
190
			}
191
		}
192
	}()
193
194
	// start API server
195
	server := rest.Server{
196
		Version:     version,
197
		TogglClient: &togglClient,
198
		Config:      &cfg,
199
		Logger:      logger,
200
	}
201
	server.Run(cfg.PortSSL)
202
}
203
204
func checkNewVersion () {
205
	if version == "" {
206
		return
207
	}
208
	versionUrl := "https://raw.githubusercontent.com/viasite/planfix-toggl-server/master/package.json"
209
	c := http.Client{
210
		Timeout: time.Second * 2,
211
	}
212
	req, err := http.NewRequest(http.MethodGet, versionUrl, nil)
213
	if err != nil {
214
		return
215
	}
216
	res, getErr := c.Do(req)
217
	if getErr != nil {
218
		return
219
	}
220
221
	body, readErr := ioutil.ReadAll(res.Body)
222
	if readErr != nil {
223
		log.Fatal(readErr)
224
	}
225
	/*if body != nil {
226
		defer body.Close()
227
	}*/
228
229
	packageJson := struct {
230
		Version string `json:"version"`
231
	}{}
232
	jsonErr := json.Unmarshal(body, &packageJson)
233
	if jsonErr != nil {
234
		log.Fatal(jsonErr)
235
	}
236
237
	if version != packageJson.Version {
238
		util.Notify(fmt.Sprintf("Доступна новая версия: %s", packageJson.Version))
239
	}
240
}
241
242
func onReady() {
243
	go initApp()
244
245
	// systray.EnableAppWindow("Lantern", 1024, 768) // in next systray versions
246
	systray.SetIcon(icon.Data)
247
	systray.SetTitle("planfix-toggl")
248
	systray.SetTooltip(fmt.Sprintf("planfix-toggl %s", version))
249
250
	trayMenu = make(map[string]*systray.MenuItem)
251
	trayMenu["send"] = systray.AddMenuItem("Sync", "")
252
	trayMenu["web"] = systray.AddMenuItem("Open web interface", "")
253
	trayMenu["current"] = systray.AddMenuItem("Open current task", "")
254
	trayMenu["log"] = systray.AddMenuItem("Open log", "")
255
	trayMenu["quit"] = systray.AddMenuItem("Quit", "Quit the whole app")
256
257
	trayMenu["send"].Disable()
258
	trayMenu["web"].Disable()
259
	trayMenu["log"].Disable()
260
}
261
262
func onExit() {
263
	systray.Quit()
264
	//os.Exit(0)
265
}
266
267
func main() {
268
	//systray.Run(onReady, onExit)
269
	systray.RunWithAppWindow("planfix-toggl", 1024, 768, onReady, onExit)
270
}
271