mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-10-20 18:17:18 +08:00
fix(json): serialize blob data
Summary: A fix to reserialize JSON blob data properly for complex object types. We should investigate overriding all of JSON.parse and JSON.stringify. This coming in a future diff. Also we were using old Electron APIs that were throwing backend errors Test Plan: todo Reviewers: juan, bengotow Reviewed By: bengotow Differential Revision: https://phab.nylas.com/D2326
This commit is contained in:
parent
17e9cc8921
commit
e275f353c1
12 changed files with 92 additions and 47 deletions
|
@ -83,7 +83,7 @@ class N1SpecReporter extends View
|
|||
@on 'click', '.stack-trace', ->
|
||||
$(this).toggleClass('expanded')
|
||||
|
||||
@reloadButton.on 'click', -> require('electron').ipcRenderer.send('call-window-method', 'restart')
|
||||
@reloadButton.on 'click', -> require('electron').ipcRenderer.send('call-webcontents-method', 'reload')
|
||||
|
||||
reportRunnerResults: (runner) ->
|
||||
@updateSpecCounts()
|
||||
|
|
|
@ -364,6 +364,11 @@ class Application
|
|||
ipcMain.on 'call-webcontents-method', (event, method, args...) ->
|
||||
event.sender[method](args...)
|
||||
|
||||
ipcMain.on 'call-devtools-webcontents-method', (event, method, args...) ->
|
||||
# If devtools aren't open the `webContents::devToolsWebContents`
|
||||
# will be null
|
||||
event.sender.devToolsWebContents?[method](args...)
|
||||
|
||||
ipcMain.on 'action-bridge-rebroadcast-to-all', (event, args...) =>
|
||||
win = BrowserWindow.fromWebContents(event.sender)
|
||||
@windowManager.windows().forEach (nylasWindow) ->
|
||||
|
|
|
@ -9,6 +9,7 @@ AttributeDateTime = require './attributes/attribute-datetime'
|
|||
AttributeCollection = require './attributes/attribute-collection'
|
||||
AttributeJoinedData = require './attributes/attribute-joined-data'
|
||||
AttributeServerId = require './attributes/attribute-serverid'
|
||||
AttributeSerializedObjects = require './attributes/attribute-serialized-objects'
|
||||
|
||||
module.exports =
|
||||
Matcher: Matcher
|
||||
|
@ -22,6 +23,7 @@ module.exports =
|
|||
Collection: -> new AttributeCollection(arguments...)
|
||||
JoinedData: -> new AttributeJoinedData(arguments...)
|
||||
ServerId: -> new AttributeServerId(arguments...)
|
||||
SerializedObjects: -> new AttributeSerializedObjects(arguments...)
|
||||
|
||||
AttributeNumber: AttributeNumber
|
||||
AttributeString: AttributeString
|
||||
|
@ -31,3 +33,4 @@ module.exports =
|
|||
AttributeCollection: AttributeCollection
|
||||
AttributeJoinedData: AttributeJoinedData
|
||||
AttributeServerId: AttributeServerId
|
||||
AttributeSerializedObjects: AttributeSerializedObjects
|
||||
|
|
|
@ -3,6 +3,10 @@ Matcher = require './matcher'
|
|||
|
||||
###
|
||||
Public: An object that can be cast to `itemClass`
|
||||
|
||||
If you don't know the `itemClass` ahead of time and are storing complex,
|
||||
typed, nested objects, use `AttributeSerializedObject` instead.
|
||||
|
||||
Section: Database
|
||||
###
|
||||
class AttributeObject extends Attribute
|
||||
|
@ -21,8 +25,9 @@ class AttributeObject extends Attribute
|
|||
if @itemClass
|
||||
obj = new @itemClass(val)
|
||||
# Important: if no ids are in the JSON, don't make them up randomly.
|
||||
# This causes an object to be "different" each time it's de-serialized
|
||||
# even if it's actually the same, makes React components re-render!
|
||||
# This causes an object to be "different" each time it's
|
||||
# de-serialized even if it's actually the same, makes React
|
||||
# components re-render!
|
||||
obj.clientId = undefined
|
||||
# Warning: typeof(null) is object
|
||||
if obj.fromJSON and val and typeof(val) is 'object'
|
||||
|
|
21
src/flux/attributes/attribute-serialized-objects.coffee
Normal file
21
src/flux/attributes/attribute-serialized-objects.coffee
Normal file
|
@ -0,0 +1,21 @@
|
|||
Utils = require '../models/utils'
|
||||
Attribute = require './attribute'
|
||||
|
||||
###
|
||||
Public: An object that is a composite of several types of objects. We
|
||||
inflate and deflate them using `Utils.deserializeRegisteredObjects` and
|
||||
`Utils.serializeRegisteredObjects`.
|
||||
|
||||
If you're storing an object of a single type, use `AttributeObject` with
|
||||
the `itemClass` option
|
||||
|
||||
Section: Database
|
||||
###
|
||||
class AttributeSerializedObjects extends Attribute
|
||||
toJSON: (val) ->
|
||||
return Utils.serializeRegisteredObjects(val)
|
||||
|
||||
fromJSON: (val) ->
|
||||
return Utils.deserializeRegisteredObjects(val)
|
||||
|
||||
module.exports = AttributeSerializedObjects
|
8
src/flux/models/json-blob-query.coffee
Normal file
8
src/flux/models/json-blob-query.coffee
Normal file
|
@ -0,0 +1,8 @@
|
|||
Utils = require './utils'
|
||||
ModelQuery = require './query'
|
||||
|
||||
class JSONBlobQuery extends ModelQuery
|
||||
formatResultObjects: (objects) =>
|
||||
return objects[0]?.json || null
|
||||
|
||||
module.exports = JSONBlobQuery
|
|
@ -17,7 +17,7 @@ class JSONBlob extends Model
|
|||
modelKey: 'serverId'
|
||||
jsonKey: 'server_id'
|
||||
|
||||
'json': Attributes.Object
|
||||
'json': Attributes.SerializedObjects
|
||||
modelKey: 'json'
|
||||
jsonKey: 'json'
|
||||
|
||||
|
|
|
@ -176,18 +176,20 @@ class ModelQuery
|
|||
return result[0]['count'] / 1
|
||||
else
|
||||
try
|
||||
objects = result.map (row) =>
|
||||
json = JSON.parse(row['data'])
|
||||
object = (new @_klass).fromJSON(json)
|
||||
for attr in @_includeJoinedData
|
||||
value = row[attr.jsonKey]
|
||||
value = null if value is AttributeJoinedData.NullPlaceholder
|
||||
object[attr.modelKey] = value
|
||||
object
|
||||
objects = result.map(@inflateRawRow)
|
||||
catch jsonError
|
||||
throw new Error("Query could not parse the database result. Query: #{@sql()}, Error: #{jsonError.toString()}")
|
||||
return objects
|
||||
|
||||
inflateRawRow: (row) =>
|
||||
json = JSON.parse(row['data'])
|
||||
object = (new @_klass).fromJSON(json)
|
||||
for attr in @_includeJoinedData
|
||||
value = row[attr.jsonKey]
|
||||
value = null if value is AttributeJoinedData.NullPlaceholder
|
||||
object[attr.modelKey] = value
|
||||
object
|
||||
|
||||
formatResultObjects: (objects) ->
|
||||
return objects[0] if @_returnOne
|
||||
return objects
|
||||
|
|
|
@ -10,35 +10,39 @@ DefaultResourcePath = null
|
|||
module.exports =
|
||||
Utils =
|
||||
|
||||
# To deserialize a string serialized with the `Utils.serializeRegisteredObjects`
|
||||
# method. This will convert anything that has a known class to its
|
||||
# appropriate object type.
|
||||
JSONReviver: (k,v) ->
|
||||
TaskRegistry ?= require '../../task-registry'
|
||||
DatabaseObjectRegistry ?= require '../../database-object-registry'
|
||||
type = v?.__constructorName
|
||||
return v unless type
|
||||
|
||||
if DatabaseObjectRegistry.isInRegistry(type)
|
||||
return DatabaseObjectRegistry.deserialize(type, v)
|
||||
|
||||
if TaskRegistry.isInRegistry(type)
|
||||
return TaskRegistry.deserialize(type, v)
|
||||
|
||||
return v
|
||||
|
||||
JSONReplacer: (k,v) ->
|
||||
TaskRegistry ?= require '../../task-registry'
|
||||
DatabaseObjectRegistry ?= require '../../database-object-registry'
|
||||
if _.isObject(v)
|
||||
type = this[k].constructor.name
|
||||
if DatabaseObjectRegistry.isInRegistry(type) or TaskRegistry.isInRegistry(type)
|
||||
v.__constructorName = type
|
||||
return v
|
||||
|
||||
# To deserialize a string serialized with the
|
||||
# `Utils.serializeRegisteredObjects` method. This will convert anything
|
||||
# that has a known class to its appropriate object type.
|
||||
## TODO: Globally override JSON.parse???
|
||||
deserializeRegisteredObjects: (json) ->
|
||||
TaskRegistry ?= require '../../task-registry'
|
||||
DatabaseObjectRegistry ?= require '../../database-object-registry'
|
||||
|
||||
JSON.parse json, (k,v) ->
|
||||
type = v?.__constructorName
|
||||
return v unless type
|
||||
|
||||
if DatabaseObjectRegistry.isInRegistry(type)
|
||||
return DatabaseObjectRegistry.deserialize(type, v)
|
||||
|
||||
if TaskRegistry.isInRegistry(type)
|
||||
return TaskRegistry.deserialize(type, v)
|
||||
|
||||
return v
|
||||
return JSON.parse(json, Utils.JSONReviver)
|
||||
|
||||
## TODO: Globally override JSON.stringify???
|
||||
serializeRegisteredObjects: (object) ->
|
||||
TaskRegistry ?= require '../../task-registry'
|
||||
DatabaseObjectRegistry ?= require '../../database-object-registry'
|
||||
|
||||
JSON.stringify object, (k, v) ->
|
||||
if _.isObject(v)
|
||||
type = this[k].constructor.name
|
||||
if DatabaseObjectRegistry.isInRegistry(type) or TaskRegistry.isInRegistry(type)
|
||||
v.__constructorName = type
|
||||
return v
|
||||
return JSON.stringify(object, Utils.JSONReplacer)
|
||||
|
||||
timeZone: tz
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ Model = require '../models/model'
|
|||
Utils = require '../models/utils'
|
||||
Actions = require '../actions'
|
||||
ModelQuery = require '../models/query'
|
||||
JSONBlobQuery = require '../models/json-blob-query'
|
||||
NylasStore = require '../../global/nylas-store'
|
||||
PromiseQueue = require 'promise-queue'
|
||||
DatabaseSetupQueryBuilder = require './database-setup-query-builder'
|
||||
|
@ -34,10 +35,6 @@ DEBUG_MISSING_ACCOUNT_ID = false
|
|||
BEGIN_TRANSACTION = 'BEGIN TRANSACTION'
|
||||
COMMIT = 'COMMIT'
|
||||
|
||||
class JSONBlobQuery extends ModelQuery
|
||||
formatResultObjects: (objects) =>
|
||||
return objects[0]?.json || null
|
||||
|
||||
###
|
||||
Public: N1 is built on top of a custom database layer modeled after
|
||||
ActiveRecord. For many parts of the application, the database is the source
|
||||
|
|
|
@ -521,7 +521,7 @@ class NylasEnvConstructor extends Model
|
|||
|
||||
# Extended: Reload the current window.
|
||||
reload: ->
|
||||
ipcRenderer.send('call-window-method', 'restart')
|
||||
ipcRenderer.send('call-webcontents-method', 'reload')
|
||||
|
||||
# Updates the window load settings - called when the app is ready to display
|
||||
# a hot-loaded window. Causes listeners registered with `onWindowPropsReceived`
|
||||
|
@ -789,15 +789,15 @@ class NylasEnvConstructor extends Model
|
|||
|
||||
# Extended: Open the dev tools for the current window.
|
||||
openDevTools: ->
|
||||
ipcRenderer.send('call-window-method', 'openDevTools')
|
||||
ipcRenderer.send('call-webcontents-method', 'openDevTools')
|
||||
|
||||
# Extended: Toggle the visibility of the dev tools for the current window.
|
||||
toggleDevTools: ->
|
||||
ipcRenderer.send('call-window-method', 'toggleDevTools')
|
||||
ipcRenderer.send('call-webcontents-method', 'toggleDevTools')
|
||||
|
||||
# Extended: Execute code in dev tools.
|
||||
executeJavaScriptInDevTools: (code) ->
|
||||
ipcRenderer.send('call-webcontents-method', 'executeJavaScriptInDevTools', code)
|
||||
ipcRenderer.send('call-devtools-webcontents-method', 'executeJavaScript', code)
|
||||
|
||||
###
|
||||
Section: Private
|
||||
|
|
|
@ -34,4 +34,4 @@ prefs.activate()
|
|||
|
||||
ipc.on 'command', (command, args) ->
|
||||
if command is 'window:toggle-dev-tools'
|
||||
ipc.send('call-window-method', 'toggleDevTools')
|
||||
ipc.send('call-webcontents-method', 'toggleDevTools')
|
||||
|
|
Loading…
Add table
Reference in a new issue