mirror of
https://github.com/Foundry376/Mailspring.git
synced 2024-09-21 07:46:06 +08:00
feat(database): Save and retrieve arbitrary json blobs to database
This fixes T2233, which was caused by the main window trying to write config.cson very often as initial sync happened, and the parent process trying to observe those changes on disk to watch for the user's API key being removed. Further refactoring would be good but this will fix it.
This commit is contained in:
parent
67281430f0
commit
d0b7f2b0dd
|
@ -158,7 +158,6 @@ class Application
|
|||
buttons: ['OK']
|
||||
fs.unlink path.join(configDirPath,'edgehill.db'), (err) =>
|
||||
@setDatabasePhase('setup')
|
||||
@config.set("nylas.sync-state", {})
|
||||
@windowManager.showMainWindow()
|
||||
|
||||
# Registers basic application commands, non-idempotent.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
_ = require 'underscore'
|
||||
NylasLongConnection = require './nylas-long-connection'
|
||||
|
||||
DatabaseStore = require './stores/database-store'
|
||||
{Publisher} = require './modules/reflux-coffee'
|
||||
CoffeeHelpers = require './coffee-helpers'
|
||||
|
||||
|
@ -18,9 +18,13 @@ class NylasSyncWorker
|
|||
|
||||
@_terminated = false
|
||||
@_connection = new NylasLongConnection(api, namespace.id)
|
||||
@_state = atom.config.get("nylas.sync-state.#{namespace.id}") ? {}
|
||||
|
||||
@_state = null
|
||||
DatabaseStore.findJSONObject("NylasSyncWorker:#{@_namespace.id}").then (json) =>
|
||||
@_state = json ? {}
|
||||
for model, modelState of @_state
|
||||
modelState.busy = false
|
||||
@resumeFetches()
|
||||
|
||||
@
|
||||
|
||||
|
@ -34,6 +38,7 @@ class NylasSyncWorker
|
|||
@_state
|
||||
|
||||
busy: ->
|
||||
return false unless @_state
|
||||
for key, state of @_state
|
||||
if state.busy
|
||||
return true
|
||||
|
@ -51,6 +56,7 @@ class NylasSyncWorker
|
|||
@
|
||||
|
||||
resumeFetches: =>
|
||||
return unless @_state
|
||||
@fetchCollection('threads')
|
||||
@fetchCollection('calendars')
|
||||
@fetchCollection('contacts')
|
||||
|
@ -61,6 +67,7 @@ class NylasSyncWorker
|
|||
@fetchCollection('folders')
|
||||
|
||||
fetchCollection: (model, options = {}) ->
|
||||
return unless @_state
|
||||
return if @_state[model]?.complete and not options.force?
|
||||
return if @_state[model]?.busy
|
||||
|
||||
|
@ -113,7 +120,7 @@ class NylasSyncWorker
|
|||
|
||||
writeState: ->
|
||||
@_writeState ?= _.debounce =>
|
||||
atom.config.set("nylas.sync-state.#{@_namespace.id}", @_state)
|
||||
DatabaseStore.persistJSONObject("NylasSyncWorker:#{@_namespace.id}", @_state)
|
||||
,100
|
||||
@_writeState()
|
||||
@trigger()
|
||||
|
|
|
@ -32,7 +32,7 @@ If you do not wish to refresh the value, do not call the callback.
|
|||
|
||||
When you create an instance of a JSONCache, you need to provide several settings:
|
||||
|
||||
- `localPath`: path on disk to keep the cache
|
||||
- `key`: A unique key identifying this object.
|
||||
|
||||
- `version`: a version number. If the local cache has a different version number
|
||||
it will be thrown out. Useful if you want to change the format of the data
|
||||
|
@ -43,35 +43,13 @@ When you create an instance of a JSONCache, you need to provide several settings
|
|||
###
|
||||
class JSONCache
|
||||
@include: CoffeeHelpers.includeModule
|
||||
|
||||
@include Publisher
|
||||
|
||||
constructor: ({@localPath, @version, @maxAge}) ->
|
||||
constructor: ({@key, @version, @maxAge}) ->
|
||||
@_value = null
|
||||
@readLocal()
|
||||
|
||||
detatch: =>
|
||||
clearInterval(@_interval) if @_interval
|
||||
|
||||
value: ->
|
||||
@_value
|
||||
|
||||
reset: ->
|
||||
fs.unlink @localPath, (err) ->
|
||||
console.error(err)
|
||||
@_value = null
|
||||
|
||||
readLocal: =>
|
||||
fs.exists @localPath, (exists) =>
|
||||
return @refresh() unless exists
|
||||
fs.readFile @localPath, (err, contents) =>
|
||||
return @refresh() unless contents and not err
|
||||
try
|
||||
json = JSON.parse(contents)
|
||||
if json.version isnt @version
|
||||
throw new Error("Outdated schema")
|
||||
if not json.time
|
||||
throw new Error("No fetch time present")
|
||||
DatabaseStore.findJSONObject(@key).then (json) =>
|
||||
return @refresh() unless json
|
||||
return @refresh() unless json.version is @version
|
||||
@_value = json.value
|
||||
@trigger()
|
||||
|
||||
|
@ -81,17 +59,14 @@ class JSONCache
|
|||
else
|
||||
setTimeout(@refresh, @maxAge - age)
|
||||
|
||||
catch err
|
||||
console.error(err)
|
||||
@reset()
|
||||
@refresh()
|
||||
value: ->
|
||||
@_value
|
||||
|
||||
writeLocal: =>
|
||||
json =
|
||||
version: @version
|
||||
time: (new Date).getTime()
|
||||
value: @_value
|
||||
fs.writeFile(@localPath, JSON.stringify(json))
|
||||
reset: ->
|
||||
DatabaseStore.persistJSONObject(@key, {})
|
||||
clearInterval(@_interval) if @_interval
|
||||
@_interval = null
|
||||
@_value = null
|
||||
|
||||
refresh: =>
|
||||
clearInterval(@_interval) if @_interval
|
||||
|
@ -99,7 +74,11 @@ class JSONCache
|
|||
|
||||
@refreshValue (newValue) =>
|
||||
@_value = newValue
|
||||
@writeLocal()
|
||||
DatabaseStore.persistJSONObject(@key, {
|
||||
version: @version
|
||||
time: (new Date).getTime()
|
||||
value: @_value
|
||||
})
|
||||
@trigger()
|
||||
|
||||
refreshValue: (callback) =>
|
||||
|
@ -108,6 +87,9 @@ class JSONCache
|
|||
|
||||
class RankingsJSONCache extends JSONCache
|
||||
|
||||
constructor: ->
|
||||
super(key: 'RankingsJSONCache', version: 1, maxAge: 60 * 60 * 1000 * 24)
|
||||
|
||||
refreshValue: (callback) =>
|
||||
return unless atom.isMainWindow()
|
||||
|
||||
|
@ -154,11 +136,7 @@ class ContactStore extends NylasStore
|
|||
@_contactCache = []
|
||||
@_namespaceId = null
|
||||
|
||||
@_rankingsCache = new RankingsJSONCache
|
||||
localPath: path.join(atom.getConfigDirPath(), 'contact-rankings.json')
|
||||
maxAge: 60 * 60 * 1000 * 24 # one day
|
||||
version: 1
|
||||
|
||||
@_rankingsCache = new RankingsJSONCache()
|
||||
@listenTo DatabaseStore, @_onDatabaseChanged
|
||||
@listenTo NamespaceStore, @_onNamespaceChanged
|
||||
@listenTo @_rankingsCache, @_sortContactsCacheWithRankings
|
||||
|
|
|
@ -22,6 +22,10 @@ class DatabaseSetupQueryBuilder
|
|||
attributes = _.values(klass.attributes)
|
||||
queries = []
|
||||
|
||||
# Add table for storing generic JSON blobs
|
||||
queries.push("CREATE TABLE IF NOT EXISTS `JSONObject` (key TEXT PRIMARY KEY, data BLOB)")
|
||||
queries.push("CREATE UNIQUE INDEX IF NOT EXISTS `JSONObject_id` ON `JSONObject` (`key`)")
|
||||
|
||||
# Identify attributes of this class that can be matched against. These
|
||||
# attributes need their own columns in the table
|
||||
columnAttributes = _.filter attributes, (attr) ->
|
||||
|
|
|
@ -503,6 +503,17 @@ class DatabaseStore extends NylasStore
|
|||
})
|
||||
@_triggerSoon({objectClass: newModel.constructor.name, objects: [oldModel, newModel], type: 'swap'})
|
||||
|
||||
persistJSONObject: (key, json) ->
|
||||
@_query(BEGIN_TRANSACTION)
|
||||
@_query("REPLACE INTO `JSONObject` (`key`,`data`) VALUES (?,?)", [key, JSON.stringify(json)])
|
||||
@_query(COMMIT)
|
||||
|
||||
findJSONObject: (key) ->
|
||||
@_query("SELECT `data` FROM `JSONObject` WHERE key = ? LIMIT 1", [key]).then (results) =>
|
||||
return Promise.resolve(null) unless results[0]
|
||||
data = JSON.parse(results[0].data)
|
||||
Promise.resolve(data)
|
||||
|
||||
########################################################################
|
||||
########################### PRIVATE METHODS ############################
|
||||
########################################################################
|
||||
|
|
Loading…
Reference in a new issue