{"action":"created","issue":{"url":"https://api.github.com/repos/syncthing/syncthing/issues/2836","repository_url":"https://api.github.com/repos/syncthing/syncthing","labels_url":"https://api.github.com/repos/syncthing/syncthing/issues/2836/labels{/name}","comments_url":"https://api.github.com/repos/syncthing/syncthing/issues/2836/comments","events_url":"https://api.github.com/repos/syncthing/syncthing/issues/2836/events","html_url":"https://github.com/syncthing/syncthing/pull/2836","id":140352031,"number":2836,"title":"[WIP] Refactor API/GUI code for testing","user":{"login":"calmh","id":125426,"avatar_url":"https://avatars.githubusercontent.com/u/125426?v=3","gravatar_id":"","url":"https://api.github.com/users/calmh","html_url":"https://github.com/calmh","followers_url":"https://api.github.com/users/calmh/followers","following_url":"https://api.github.com/users/calmh/following{/other_user}","gists_url":"https://api.github.com/users/calmh/gists{/gist_id}","starred_url":"https://api.github.com/users/calmh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/calmh/subscriptions","organizations_url":"https://api.github.com/users/calmh/orgs","repos_url":"https://api.github.com/users/calmh/repos","events_url":"https://api.github.com/users/calmh/events{/privacy}","received_events_url":"https://api.github.com/users/calmh/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":2,"created_at":"2016-03-12T06:18:30Z","updated_at":"2016-03-12T10:42:24Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/syncthing/syncthing/pulls/2836","html_url":"https://github.com/syncthing/syncthing/pull/2836","diff_url":"https://github.com/syncthing/syncthing/pull/2836.diff","patch_url":"https://github.com/syncthing/syncthing/pull/2836.patch"},"body":"**NOT TO BE MERGED** Just to show some thinking and get some feedback...\r\n\r\nI converted a number of concrete types to interfaces (events.BufferedSubscription, discover.CachingMux, relay.Service, logger.Recorder). These are the ones passed into the apiService constructor, so having them be interfaces mean they can be mocked out when testing. That part is fairly clean I think.\r\n\r\nThen I extracted a Config interface (*config.Wrapper) and a Model interface (from *model.Model). These are quite large, specifically the Model interface is fucking huuuge. Of course this is an effect of the Model type itself being too large. But there are a few distinct categories of methods on it - ones related to quering sizes and statistics, ones related to connection management, ones related to scanning, etc. Potentially these could be broken out into smaller interfaces, all of which could be fulfilled by an actual *model.Model today, but could potentially be separated out later. I haven't fully thought that through yet..."},"comment":{"url":"https://api.github.com/repos/syncthing/syncthing/issues/comments/195713627","html_url":"https://github.com/syncthing/syncthing/pull/2836#issuecomment-195713627","issue_url":"https://api.github.com/repos/syncthing/syncthing/issues/2836","id":195713627,"user":{"login":"calmh","id":125426,"avatar_url":"https://avatars.githubusercontent.com/u/125426?v=3","gravatar_id":"","url":"https://api.github.com/users/calmh","html_url":"https://github.com/calmh","followers_url":"https://api.github.com/users/calmh/followers","following_url":"https://api.github.com/users/calmh/following{/other_user}","gists_url":"https://api.github.com/users/calmh/gists{/gist_id}","starred_url":"https://api.github.com/users/calmh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/calmh/subscriptions","organizations_url":"https://api.github.com/users/calmh/orgs","repos_url":"https://api.github.com/users/calmh/repos","events_url":"https://api.github.com/users/calmh/events{/privacy}","received_events_url":"https://api.github.com/users/calmh/received_events","type":"User","site_admin":false},"created_at":"2016-03-12T10:42:24Z","updated_at":"2016-03-12T10:42:24Z","body":"Mostly because this\r\n\r\n```diff\r\n +type Model interface {\r\n +\tGlobalDirectoryTree(folder, prefix string, levels int, dirsonly bool) map[string]interface{}\r\n +\tCompletion(device protocol.DeviceID, folder string) float64\r\n +\tOverride(folder string)\r\n +\tNeedFolderFiles(folder string, page, perpage int) ([]db.FileInfoTruncated, []db.FileInfoTruncated, []db.FileInfoTruncated, int)\r\n +\tNeedSize(folder string) (nfiles int, bytes int64)\r\n +\tConnectionStats() map[string]interface{}\r\n +\tDeviceStatistics() map[string]stats.DeviceStatistics\r\n +\tFolderStatistics() map[string]stats.FolderStatistics\r\n +\tCurrentFolderFile(folder string, file string) (protocol.FileInfo, bool)\r\n +\tCurrentGlobalFile(folder string, file string) (protocol.FileInfo, bool)\r\n +\tResetFolder(folder string)\r\n +\tAvailability(folder, file string) []protocol.DeviceID\r\n +\tGetIgnores(folder string) ([]string, []string, error)\r\n +\tSetIgnores(folder string, content []string) error\r\n +\tPauseDevice(device protocol.DeviceID)\r\n +\tResumeDevice(device protocol.DeviceID)\r\n +\tDelayScan(folder string, next time.Duration)\r\n +\tScanFolder(folder string) error\r\n +\tScanFolders() map[string]error\r\n +\tScanFolderSubs(folder string, subs []string) error\r\n +\tBringToFront(folder, file string)\r\n +\tConnectedTo(deviceID protocol.DeviceID) bool\r\n +\tGlobalSize(folder string) (nfiles, deleted int, bytes int64)\r\n +\tLocalSize(folder string) (nfiles, deleted int, bytes int64)\r\n +\tCurrentLocalVersion(folder string) (int64, bool)\r\n +\tRemoteLocalVersion(folder string) (int64, bool)\r\n +\tState(folder string) (string, time.Time, error)\r\n +}\r\n```\r\n\r\nis :scream: \r\n\r\nBut maybe that's just the way it is until we maybe, hopefully, at some point refactor that a bit."}}