fix(delta-sync): Properly look up model types, add specs for all this logic

Summary: Emergency fix for broken logic consuming delta updates. Also includes specs for untested functionality.

Test Plan: Run new tests

Reviewers: dillon, evan

Reviewed By: evan

Differential Revision: https://phab.nylas.com/D1939
This commit is contained in:
Ben Gotow 2015-08-26 11:43:10 -07:00
parent 6670d10ee4
commit 91774668eb
4 changed files with 1191 additions and 52 deletions

View file

@ -0,0 +1,447 @@
{
"create": {
"message": {
"9p571g0ie63rg0ekec699ol5e": {
"body": "<<BODY>>",
"files": [],
"from": [
{
"name": "Karen Rustad Tölva",
"email": "karen.rustad@gmail.com"
}
],
"account_id": "9jx3zd30wqx04p26vh09ptox1",
"thread_id": "62ehtebypazlgokcjvo8o9yak",
"cc": [],
"object": "message",
"bcc": [],
"snippet": "<<SNIPPET>>",
"to": [
{
"name": "",
"email": "ben@nylas.com"
}
],
"folder": {
"display_name": "Inbox",
"id": "bn7z083ho0mqfhqd68tio5a70",
"name": "inbox"
},
"date": 1440610192,
"reply_to": [],
"events": [],
"starred": false,
"unread": true,
"id": "9p571g0ie63rg0ekec699ol5e",
"subject": "This is an email following up on the Electron meetup."
},
"4rv7upa3gzuf1hal48b15lxrx": {
"body": "<<BODY>>",
"files": [],
"from": [
{
"name": "evan (Evan Morikawa)",
"email": "noreply+phabricator@nilas.com"
}
],
"account_id": "9jx3zd30wqx04p26vh09ptox1",
"thread_id": "cungn0trv89l19mf08a2blyuh",
"cc": [],
"object": "message",
"bcc": [],
"snippet": "<<SNIPPET>>",
"to": [
{
"name": "",
"email": "ben@inboxapp.com"
}
],
"folder": {
"display_name": "Inbox",
"id": "bn7z083ho0mqfhqd68tio5a70",
"name": "inbox"
},
"date": 1440609916,
"reply_to": [],
"events": [],
"starred": false,
"unread": true,
"id": "4rv7upa3gzuf1hal48b15lxrx",
"subject": "[Differential] [Accepted] D1936: feat(work): Create the \"Work\" window, move TaskQueue, Nylas sync workers"
},
"tt4i92q8rtpgrbxaq6viqlf4": {
"body": "<<BODY>>",
"files": [],
"from": [
{
"name": "Ismail Pelaseyed",
"email": "ismail@sendcase.com"
}
],
"account_id": "9jx3zd30wqx04p26vh09ptox1",
"thread_id": "8qesqxoftrd3nqiig3cz6gu49",
"cc": [],
"object": "message",
"bcc": [],
"snippet": "<<SNIPPET>>",
"to": [
{
"name": "",
"email": "support@nylas.com"
}
],
"folder": {
"display_name": "Inbox",
"id": "bn7z083ho0mqfhqd68tio5a70",
"name": "inbox"
},
"date": 1440609839,
"reply_to": [],
"events": [],
"starred": false,
"unread": true,
"id": "tt4i92q8rtpgrbxaq6viqlf4",
"subject": "Re: Incoming webhook POST body empty"
},
"7q4wtqzj3nxb42y6ysbl5l73t": {
"body": "<<BODY>>",
"files": [],
"from": [
{
"name": "Ismail Pelaseyed",
"email": "ismail@sendcase.com"
}
],
"account_id": "9jx3zd30wqx04p26vh09ptox1",
"thread_id": "auro0q0gn0eawfrmqgzbi4f53",
"cc": [],
"object": "message",
"bcc": [],
"snippet": "<<SNIPPET>>",
"to": [
{
"name": "",
"email": "support@nylas.com"
}
],
"folder": {
"display_name": "Deleted Items",
"id": "b5t18ldx25xibwq5ctuh10u8h",
"name": "trash"
},
"date": 1440609839,
"reply_to": [],
"events": [],
"starred": false,
"unread": true,
"id": "7q4wtqzj3nxb42y6ysbl5l73t",
"subject": "Re: Incoming webhook POST body empty"
},
"4oesh7fhsbd45t7dx30p03vz1": {
"body": "<<BODY>>",
"files": [],
"from": [
{
"name": "Ismail Pelaseyed",
"email": "ismail@sendcase.com"
}
],
"account_id": "9jx3zd30wqx04p26vh09ptox1",
"thread_id": "auro0q0gn0eawfrmqgzbi4f53",
"cc": [],
"object": "message",
"bcc": [],
"snippet": "<<SNIPPET>>",
"to": [
{
"name": "",
"email": "support@nylas.com"
}
],
"folder": {
"display_name": "Deleted Items",
"id": "b5t18ldx25xibwq5ctuh10u8h",
"name": "trash"
},
"date": 1440594160,
"reply_to": [],
"events": [],
"starred": false,
"unread": true,
"id": "4oesh7fhsbd45t7dx30p03vz1",
"subject": "Incoming webhook POST body empty"
}
},
"thread": {
"62ehtebypazlgokcjvo8o9yak": {
"folders": [
{
"display_name": "Inbox",
"id": "bn7z083ho0mqfhqd68tio5a70",
"name": "inbox"
}
],
"object": "thread",
"account_id": "9jx3zd30wqx04p26vh09ptox1",
"tags": [
{
"name": "Inbox",
"id": "inbox"
},
{
"name": "unread",
"id": "unread"
}
],
"last_message_timestamp": 1440610192,
"has_attachments": false,
"first_message_timestamp": 1440610192,
"id": "62ehtebypazlgokcjvo8o9yak",
"subject": "This is an email following up on the Electron meetup.",
"last_message_received_timestamp": 1440610192,
"message_ids": [
"9p571g0ie63rg0ekec699ol5e"
],
"snippet": "<<SNIPPET>>",
"participants": [
{
"name": "",
"email": "ben@nylas.com"
},
{
"name": "Karen Rustad Tölva",
"email": "karen.rustad@gmail.com"
}
],
"version": 0,
"starred": false,
"unread": true,
"draft_ids": []
},
"auro0q0gn0eawfrmqgzbi4f53": {
"folders": [
{
"display_name": "Deleted Items",
"id": "b5t18ldx25xibwq5ctuh10u8h",
"name": "trash"
}
],
"object": "thread",
"account_id": "9jx3zd30wqx04p26vh09ptox1",
"tags": [
{
"name": "Deleted Items",
"id": "trash"
},
{
"name": "unread",
"id": "unread"
}
],
"last_message_timestamp": 1440609839,
"has_attachments": false,
"first_message_timestamp": 1440594160,
"id": "auro0q0gn0eawfrmqgzbi4f53",
"subject": "Incoming webhook POST body empty",
"last_message_received_timestamp": 1440609839,
"message_ids": [
"4oesh7fhsbd45t7dx30p03vz1",
"7q4wtqzj3nxb42y6ysbl5l73t"
],
"snippet": "<<SNIPPET>>",
"participants": [
{
"name": "",
"email": "support@nylas.com"
},
{
"name": "Ismail Pelaseyed",
"email": "ismail@sendcase.com"
}
],
"version": 1,
"starred": false,
"unread": true,
"draft_ids": []
}
},
"contact": {
"1faoqpnn2rhf6mstmyc0ve71u": {
"email": "karen.rustad@gmail.com",
"object": "contact",
"id": "1faoqpnn2rhf6mstmyc0ve71u",
"name": "Karen Rustad Tölva",
"account_id": "9jx3zd30wqx04p26vh09ptox1"
}
}
},
"modify": {
"thread": {
"cungn0trv89l19mf08a2blyuh": {
"folders": [
{
"display_name": "Inbox",
"id": "bn7z083ho0mqfhqd68tio5a70",
"name": "inbox"
}
],
"object": "thread",
"account_id": "9jx3zd30wqx04p26vh09ptox1",
"tags": [
{
"name": "Inbox",
"id": "inbox"
},
{
"name": "unread",
"id": "unread"
}
],
"last_message_timestamp": 1440609916,
"has_attachments": false,
"first_message_timestamp": 1440543552,
"id": "cungn0trv89l19mf08a2blyuh",
"subject": "[Differential] [Request, 1,621 lines] D1936: feat(work): Create the \"Work\" window, move TaskQueue, Nylas sync workers",
"last_message_received_timestamp": 1440609916,
"message_ids": [
"9uznoal93shahahlkesxkmfkr",
"4rv7upa3gzuf1hal48b15lxrx"
],
"snippet": "<<SNIPPET>>",
"participants": [
{
"name": "",
"email": "ben@inboxapp.com"
},
{
"name": "evan (Evan Morikawa)",
"email": "noreply+phabricator@nilas.com"
},
{
"name": "bengotow (Ben Gotow)",
"email": "noreply+phabricator@nilas.com"
}
],
"version": 1,
"starred": false,
"unread": true,
"draft_ids": []
},
"8qesqxoftrd3nqiig3cz6gu49": {
"folders": [
{
"display_name": "Inbox",
"id": "bn7z083ho0mqfhqd68tio5a70",
"name": "inbox"
}
],
"object": "thread",
"account_id": "9jx3zd30wqx04p26vh09ptox1",
"tags": [
{
"name": "Inbox",
"id": "inbox"
},
{
"name": "unread",
"id": "unread"
}
],
"last_message_timestamp": 1440609839,
"has_attachments": false,
"first_message_timestamp": 1440594160,
"id": "8qesqxoftrd3nqiig3cz6gu49",
"subject": "Incoming webhook POST body empty",
"last_message_received_timestamp": 1440609839,
"message_ids": [
"b2dq8u1kbambwor2vy5nz619n",
"tt4i92q8rtpgrbxaq6viqlf4"
],
"snippet": "<<SNIPPET>>",
"participants": [
{
"name": "",
"email": "support@nylas.com"
},
{
"name": "Ismail Pelaseyed",
"email": "ismail@sendcase.com"
}
],
"version": 1,
"starred": false,
"unread": true,
"draft_ids": []
},
"auro0q0gn0eawfrmqgzbi4f53": {
"folders": [
{
"display_name": "Deleted Items",
"id": "b5t18ldx25xibwq5ctuh10u8h",
"name": "trash"
}
],
"object": "thread",
"account_id": "9jx3zd30wqx04p26vh09ptox1",
"tags": [
{
"name": "Deleted Items",
"id": "trash"
},
{
"name": "unread",
"id": "unread"
}
],
"last_message_timestamp": 1440609839,
"has_attachments": false,
"first_message_timestamp": 1440594160,
"id": "auro0q0gn0eawfrmqgzbi4f53",
"subject": "Incoming webhook POST body empty",
"last_message_received_timestamp": 1440609839,
"message_ids": [
"4oesh7fhsbd45t7dx30p03vz1",
"7q4wtqzj3nxb42y6ysbl5l73t"
],
"snippet": "<<SNIPPET>>",
"participants": [
{
"name": "",
"email": "support@nylas.com"
},
{
"name": "Ismail Pelaseyed",
"email": "ismail@sendcase.com"
}
],
"version": 1,
"starred": false,
"unread": true,
"draft_ids": []
}
}
},
"destroy": [
{
"cursor": "bb95ddzqtr2gpmvgrng73t6ih",
"object": "thread",
"event": "delete",
"id": "8qesqxoftrd3nqiig3cz6gu49",
"timestamp": "2015-08-26T17:36:45.297Z"
},
{
"cursor": "f1pw0buzv336n5xbuod7579cv",
"object": "message",
"event": "delete",
"id": "tt4i92q8rtpgrbxaq6viqlf4",
"timestamp": "2015-08-26T17:36:45.297Z"
},
{
"cursor": "du99szyqlrujornwr5fgrzxeg",
"object": "message",
"event": "delete",
"id": "b2dq8u1kbambwor2vy5nz619n",
"timestamp": "2015-08-26T17:36:45.297Z"
}
]
}

View file

@ -0,0 +1,510 @@
[
{
"cursor": "9w3el67207f8vvbkv89os5ay6",
"attributes": {
"body": "<<BODY>>",
"files": [],
"from": [
{
"name": "Karen Rustad Tölva",
"email": "karen.rustad@gmail.com"
}
],
"account_id": "9jx3zd30wqx04p26vh09ptox1",
"thread_id": "62ehtebypazlgokcjvo8o9yak",
"cc": [],
"object": "message",
"bcc": [],
"snippet": "<<SNIPPET>>",
"to": [
{
"name": "",
"email": "ben@nylas.com"
}
],
"folder": {
"display_name": "Inbox",
"id": "bn7z083ho0mqfhqd68tio5a70",
"name": "inbox"
},
"date": 1440610192,
"reply_to": [],
"events": [],
"starred": false,
"unread": true,
"id": "9p571g0ie63rg0ekec699ol5e",
"subject": "This is an email following up on the Electron meetup."
},
"object": "message",
"event": "create",
"id": "9p571g0ie63rg0ekec699ol5e",
"timestamp": "2015-08-26T17:35:16.506Z"
},
{
"cursor": "bqw8x58pghty4fw828tezb0va",
"attributes": {
"folders": [
{
"display_name": "Inbox",
"id": "bn7z083ho0mqfhqd68tio5a70",
"name": "inbox"
}
],
"object": "thread",
"account_id": "9jx3zd30wqx04p26vh09ptox1",
"tags": [
{
"name": "Inbox",
"id": "inbox"
},
{
"name": "unread",
"id": "unread"
}
],
"last_message_timestamp": 1440610192,
"has_attachments": false,
"first_message_timestamp": 1440610192,
"id": "62ehtebypazlgokcjvo8o9yak",
"subject": "This is an email following up on the Electron meetup.",
"last_message_received_timestamp": 1440610192,
"message_ids": [
"9p571g0ie63rg0ekec699ol5e"
],
"snippet": "<<SNIPPET>>",
"participants": [
{
"name": "",
"email": "ben@nylas.com"
},
{
"name": "Karen Rustad Tölva",
"email": "karen.rustad@gmail.com"
}
],
"version": 0,
"starred": false,
"unread": true,
"draft_ids": []
},
"object": "thread",
"event": "create",
"id": "62ehtebypazlgokcjvo8o9yak",
"timestamp": "2015-08-26T17:35:16.506Z"
},
{
"cursor": "ewa1rr5nczdvrwpu11d3454ee",
"attributes": {
"email": "karen.rustad@gmail.com",
"object": "contact",
"id": "1faoqpnn2rhf6mstmyc0ve71u",
"name": "Karen Rustad Tölva",
"account_id": "9jx3zd30wqx04p26vh09ptox1"
},
"object": "contact",
"event": "create",
"id": "1faoqpnn2rhf6mstmyc0ve71u",
"timestamp": "2015-08-26T17:35:16.506Z"
},
{
"cursor": "756iwtaiufb6vxbtp4lt57cx0",
"attributes": {
"folders": [
{
"display_name": "Inbox",
"id": "bn7z083ho0mqfhqd68tio5a70",
"name": "inbox"
}
],
"object": "thread",
"account_id": "9jx3zd30wqx04p26vh09ptox1",
"tags": [
{
"name": "Inbox",
"id": "inbox"
},
{
"name": "unread",
"id": "unread"
}
],
"last_message_timestamp": 1440609916,
"has_attachments": false,
"first_message_timestamp": 1440543552,
"id": "cungn0trv89l19mf08a2blyuh",
"subject": "[Differential] [Request, 1,621 lines] D1936: feat(work): Create the \"Work\" window, move TaskQueue, Nylas sync workers",
"last_message_received_timestamp": 1440609916,
"message_ids": [
"9uznoal93shahahlkesxkmfkr",
"4rv7upa3gzuf1hal48b15lxrx"
],
"snippet": "<<SNIPPET>>",
"participants": [
{
"name": "",
"email": "ben@inboxapp.com"
},
{
"name": "evan (Evan Morikawa)",
"email": "noreply+phabricator@nilas.com"
},
{
"name": "bengotow (Ben Gotow)",
"email": "noreply+phabricator@nilas.com"
}
],
"version": 1,
"starred": false,
"unread": true,
"draft_ids": []
},
"object": "thread",
"event": "modify",
"id": "cungn0trv89l19mf08a2blyuh",
"timestamp": "2015-08-26T17:35:16.506Z"
},
{
"cursor": "ev9ewky31ps7deaf0dm90f4qa",
"attributes": {
"body": "<<BODY>>",
"files": [],
"from": [
{
"name": "evan (Evan Morikawa)",
"email": "noreply+phabricator@nilas.com"
}
],
"account_id": "9jx3zd30wqx04p26vh09ptox1",
"thread_id": "cungn0trv89l19mf08a2blyuh",
"cc": [],
"object": "message",
"bcc": [],
"snippet": "<<SNIPPET>>",
"to": [
{
"name": "",
"email": "ben@inboxapp.com"
}
],
"folder": {
"display_name": "Inbox",
"id": "bn7z083ho0mqfhqd68tio5a70",
"name": "inbox"
},
"date": 1440609916,
"reply_to": [],
"events": [],
"starred": false,
"unread": true,
"id": "4rv7upa3gzuf1hal48b15lxrx",
"subject": "[Differential] [Accepted] D1936: feat(work): Create the \"Work\" window, move TaskQueue, Nylas sync workers"
},
"object": "message",
"event": "create",
"id": "4rv7upa3gzuf1hal48b15lxrx",
"timestamp": "2015-08-26T17:35:16.506Z"
},
{
"cursor": "3gynk12o31m2199ppja76nkve",
"attributes": {
"folders": [
{
"display_name": "Inbox",
"id": "bn7z083ho0mqfhqd68tio5a70",
"name": "inbox"
}
],
"object": "thread",
"account_id": "9jx3zd30wqx04p26vh09ptox1",
"tags": [
{
"name": "Inbox",
"id": "inbox"
},
{
"name": "unread",
"id": "unread"
}
],
"last_message_timestamp": 1440609839,
"has_attachments": false,
"first_message_timestamp": 1440594160,
"id": "8qesqxoftrd3nqiig3cz6gu49",
"subject": "Incoming webhook POST body empty",
"last_message_received_timestamp": 1440609839,
"message_ids": [
"b2dq8u1kbambwor2vy5nz619n",
"tt4i92q8rtpgrbxaq6viqlf4"
],
"snippet": "<<SNIPPET>>",
"participants": [
{
"name": "",
"email": "support@nylas.com"
},
{
"name": "Ismail Pelaseyed",
"email": "ismail@sendcase.com"
}
],
"version": 1,
"starred": false,
"unread": true,
"draft_ids": []
},
"object": "thread",
"event": "modify",
"id": "8qesqxoftrd3nqiig3cz6gu49",
"timestamp": "2015-08-26T17:35:16.506Z"
},
{
"cursor": "e8wnx217l8v1qx205d4hxvlk1",
"attributes": {
"body": "<<BODY>>",
"files": [],
"from": [
{
"name": "Ismail Pelaseyed",
"email": "ismail@sendcase.com"
}
],
"account_id": "9jx3zd30wqx04p26vh09ptox1",
"thread_id": "8qesqxoftrd3nqiig3cz6gu49",
"cc": [],
"object": "message",
"bcc": [],
"snippet": "<<SNIPPET>>",
"to": [
{
"name": "",
"email": "support@nylas.com"
}
],
"folder": {
"display_name": "Inbox",
"id": "bn7z083ho0mqfhqd68tio5a70",
"name": "inbox"
},
"date": 1440609839,
"reply_to": [],
"events": [],
"starred": false,
"unread": true,
"id": "tt4i92q8rtpgrbxaq6viqlf4",
"subject": "Re: Incoming webhook POST body empty"
},
"object": "message",
"event": "create",
"id": "tt4i92q8rtpgrbxaq6viqlf4",
"timestamp": "2015-08-26T17:35:16.506Z"
},
{
"cursor": "asyt17hyf9shing0tbdc1s5n9",
"attributes": {
"folders": [
{
"display_name": "Deleted Items",
"id": "b5t18ldx25xibwq5ctuh10u8h",
"name": "trash"
}
],
"object": "thread",
"account_id": "9jx3zd30wqx04p26vh09ptox1",
"tags": [
{
"name": "Deleted Items",
"id": "trash"
},
{
"name": "unread",
"id": "unread"
}
],
"last_message_timestamp": 1440609839,
"has_attachments": false,
"first_message_timestamp": 1440594160,
"id": "auro0q0gn0eawfrmqgzbi4f53",
"subject": "Incoming webhook POST body empty",
"last_message_received_timestamp": 1440609839,
"message_ids": [
"4oesh7fhsbd45t7dx30p03vz1",
"7q4wtqzj3nxb42y6ysbl5l73t"
],
"snippet": "<<SNIPPET>>",
"participants": [
{
"name": "",
"email": "support@nylas.com"
},
{
"name": "Ismail Pelaseyed",
"email": "ismail@sendcase.com"
}
],
"version": 1,
"starred": false,
"unread": true,
"draft_ids": []
},
"object": "thread",
"event": "modify",
"id": "auro0q0gn0eawfrmqgzbi4f53",
"timestamp": "2015-08-26T17:36:45.297Z"
},
{
"cursor": "2r2f2indqv6fc9zptw9y49h97",
"attributes": {
"body": "<<BODY>>",
"files": [],
"from": [
{
"name": "Ismail Pelaseyed",
"email": "ismail@sendcase.com"
}
],
"account_id": "9jx3zd30wqx04p26vh09ptox1",
"thread_id": "auro0q0gn0eawfrmqgzbi4f53",
"cc": [],
"object": "message",
"bcc": [],
"snippet": "<<SNIPPET>>",
"to": [
{
"name": "",
"email": "support@nylas.com"
}
],
"folder": {
"display_name": "Deleted Items",
"id": "b5t18ldx25xibwq5ctuh10u8h",
"name": "trash"
},
"date": 1440609839,
"reply_to": [],
"events": [],
"starred": false,
"unread": true,
"id": "7q4wtqzj3nxb42y6ysbl5l73t",
"subject": "Re: Incoming webhook POST body empty"
},
"object": "message",
"event": "create",
"id": "7q4wtqzj3nxb42y6ysbl5l73t",
"timestamp": "2015-08-26T17:36:45.297Z"
},
{
"cursor": "ahvjhhsorjc0qe0snhwqydvui",
"attributes": {
"folders": [
{
"display_name": "Deleted Items",
"id": "b5t18ldx25xibwq5ctuh10u8h",
"name": "trash"
}
],
"object": "thread",
"account_id": "9jx3zd30wqx04p26vh09ptox1",
"tags": [
{
"name": "Deleted Items",
"id": "trash"
},
{
"name": "unread",
"id": "unread"
}
],
"last_message_timestamp": 1440609839,
"has_attachments": false,
"first_message_timestamp": 1440594160,
"id": "auro0q0gn0eawfrmqgzbi4f53",
"subject": "Incoming webhook POST body empty",
"last_message_received_timestamp": 1440609839,
"message_ids": [
"4oesh7fhsbd45t7dx30p03vz1",
"7q4wtqzj3nxb42y6ysbl5l73t"
],
"snippet": "<<SNIPPET>>",
"participants": [
{
"name": "",
"email": "support@nylas.com"
},
{
"name": "Ismail Pelaseyed",
"email": "ismail@sendcase.com"
}
],
"version": 1,
"starred": false,
"unread": true,
"draft_ids": []
},
"object": "thread",
"event": "create",
"id": "auro0q0gn0eawfrmqgzbi4f53",
"timestamp": "2015-08-26T17:36:45.297Z"
},
{
"cursor": "2e35c6lcfvsx165kuu5534zng",
"attributes": {
"body": "<<BODY>>",
"files": [],
"from": [
{
"name": "Ismail Pelaseyed",
"email": "ismail@sendcase.com"
}
],
"account_id": "9jx3zd30wqx04p26vh09ptox1",
"thread_id": "auro0q0gn0eawfrmqgzbi4f53",
"cc": [],
"object": "message",
"bcc": [],
"snippet": "<<SNIPPET>>",
"to": [
{
"name": "",
"email": "support@nylas.com"
}
],
"folder": {
"display_name": "Deleted Items",
"id": "b5t18ldx25xibwq5ctuh10u8h",
"name": "trash"
},
"date": 1440594160,
"reply_to": [],
"events": [],
"starred": false,
"unread": true,
"id": "4oesh7fhsbd45t7dx30p03vz1",
"subject": "Incoming webhook POST body empty"
},
"object": "message",
"event": "create",
"id": "4oesh7fhsbd45t7dx30p03vz1",
"timestamp": "2015-08-26T17:36:45.297Z"
},
{
"cursor": "bb95ddzqtr2gpmvgrng73t6ih",
"object": "thread",
"event": "delete",
"id": "8qesqxoftrd3nqiig3cz6gu49",
"timestamp": "2015-08-26T17:36:45.297Z"
},
{
"cursor": "f1pw0buzv336n5xbuod7579cv",
"object": "message",
"event": "delete",
"id": "tt4i92q8rtpgrbxaq6viqlf4",
"timestamp": "2015-08-26T17:36:45.297Z"
},
{
"cursor": "du99szyqlrujornwr5fgrzxeg",
"object": "message",
"event": "delete",
"id": "b2dq8u1kbambwor2vy5nz619n",
"timestamp": "2015-08-26T17:36:45.297Z"
}
]

View file

@ -0,0 +1,182 @@
_ = require 'underscore'
fs = require 'fs'
Actions = require '../src/flux/actions'
NylasAPI = require '../src/flux/nylas-api'
Thread = require '../src/flux/models/thread'
DatabaseStore = require '../src/flux/stores/database-store'
describe "NylasAPI", ->
describe "handleModel404", ->
it "should unpersist the model from the cache that was requested", ->
model = new Thread(id: 'threadidhere')
spyOn(DatabaseStore, 'unpersistModel')
spyOn(DatabaseStore, 'find').andCallFake (klass, id) =>
return Promise.resolve(model)
NylasAPI._handleModel404("/threads/#{model.id}")
advanceClock()
expect(DatabaseStore.find).toHaveBeenCalledWith(Thread, model.id)
expect(DatabaseStore.unpersistModel).toHaveBeenCalledWith(model)
it "should not do anything if the model is not in the cache", ->
spyOn(DatabaseStore, 'unpersistModel')
spyOn(DatabaseStore, 'find').andCallFake (klass, id) =>
return Promise.resolve(null)
NylasAPI._handleModel404("/threads/1234")
advanceClock()
expect(DatabaseStore.find).toHaveBeenCalledWith(Thread, '1234')
expect(DatabaseStore.unpersistModel).not.toHaveBeenCalledWith()
it "should not do anything bad if it doesn't recognize the class", ->
spyOn(DatabaseStore, 'find')
spyOn(DatabaseStore, 'unpersistModel')
waitsForPromise ->
NylasAPI._handleModel404("/asdasdasd/1234")
runs ->
expect(DatabaseStore.find).not.toHaveBeenCalled()
expect(DatabaseStore.unpersistModel).not.toHaveBeenCalled()
it "should not do anything bad if the endpoint only has a single segment", ->
spyOn(DatabaseStore, 'find')
spyOn(DatabaseStore, 'unpersistModel')
waitsForPromise ->
NylasAPI._handleModel404("/account")
runs ->
expect(DatabaseStore.find).not.toHaveBeenCalled()
expect(DatabaseStore.unpersistModel).not.toHaveBeenCalled()
describe "handle401", ->
it "should post a notification", ->
spyOn(Actions, 'postNotification')
NylasAPI._handle401('/threads/1234')
expect(Actions.postNotification).toHaveBeenCalled()
expect(Actions.postNotification.mostRecentCall.args[0].message).toEqual("Nylas can no longer authenticate with your mail provider. You will not be able to send or receive mail. Please log out and sign in again.")
describe "handleDeltas", ->
beforeEach ->
@sampleDeltas = JSON.parse(fs.readFileSync('./spec-nylas/fixtures/delta-sync/sample.json'))
@sampleClustered = JSON.parse(fs.readFileSync('./spec-nylas/fixtures/delta-sync/sample-clustered.json'))
it "should immediately fire the received raw deltas event", ->
spyOn(Actions, 'longPollReceivedRawDeltas')
spyOn(NylasAPI, '_clusterDeltas').andReturn({create: {}, modify: {}, destroy: []})
NylasAPI._handleDeltas(@sampleDeltas)
expect(Actions.longPollReceivedRawDeltas).toHaveBeenCalled()
it "should call helper methods for all creates first, then modifications, then destroys", ->
spyOn(Actions, 'longPollProcessedDeltas')
handleDeltaDeletionPromises = []
resolveDeltaDeletionPromises = ->
fn() for fn in handleDeltaDeletionPromises
handleDeltaDeletionPromises = []
spyOn(NylasAPI, '_handleDeltaDeletion').andCallFake ->
new Promise (resolve, reject) ->
handleDeltaDeletionPromises.push(resolve)
handleModelResponsePromises = []
resolveModelResponsePromises = ->
fn() for fn in handleModelResponsePromises
handleModelResponsePromises = []
spyOn(NylasAPI, '_handleModelResponse').andCallFake ->
new Promise (resolve, reject) ->
handleModelResponsePromises.push(resolve)
NylasAPI._handleDeltas(@sampleDeltas)
createTypes = Object.keys(@sampleClustered['create'])
expect(NylasAPI._handleModelResponse.calls.length).toEqual(createTypes.length)
expect(NylasAPI._handleModelResponse.calls[0].args[0]).toEqual(_.values(@sampleClustered['create'][createTypes[0]]))
expect(NylasAPI._handleDeltaDeletion.calls.length).toEqual(0)
NylasAPI._handleModelResponse.reset()
resolveModelResponsePromises()
advanceClock()
modifyTypes = Object.keys(@sampleClustered['modify'])
expect(NylasAPI._handleModelResponse.calls.length).toEqual(modifyTypes.length)
expect(NylasAPI._handleModelResponse.calls[0].args[0]).toEqual(_.values(@sampleClustered['modify'][modifyTypes[0]]))
expect(NylasAPI._handleDeltaDeletion.calls.length).toEqual(0)
NylasAPI._handleModelResponse.reset()
resolveModelResponsePromises()
advanceClock()
destroyCount = @sampleClustered['destroy'].length
expect(NylasAPI._handleDeltaDeletion.calls.length).toEqual(destroyCount)
expect(NylasAPI._handleDeltaDeletion.calls[0].args[0]).toEqual(@sampleClustered['destroy'][0])
expect(Actions.longPollProcessedDeltas).not.toHaveBeenCalled()
resolveDeltaDeletionPromises()
advanceClock()
expect(Actions.longPollProcessedDeltas).toHaveBeenCalled()
describe "clusterDeltas", ->
beforeEach ->
@sampleDeltas = JSON.parse(fs.readFileSync('./spec-nylas/fixtures/delta-sync/sample.json'))
@expectedClustered = JSON.parse(fs.readFileSync('./spec-nylas/fixtures/delta-sync/sample-clustered.json'))
it "should collect create/modify events into a hash by model type", ->
{create, modify} = NylasAPI._clusterDeltas(@sampleDeltas)
expect(create).toEqual(@expectedClustered.create)
expect(modify).toEqual(@expectedClustered.modify)
it "should collect destroys into an array", ->
{destroy} = NylasAPI._clusterDeltas(@sampleDeltas)
expect(destroy).toEqual(@expectedClustered.destroy)
describe "handleDeltaDeletion", ->
beforeEach ->
@thread = new Thread(id: 'idhere')
@delta =
"cursor": "bb95ddzqtr2gpmvgrng73t6ih",
"object": "thread",
"event": "delete",
"id": @thread.id,
"timestamp": "2015-08-26T17:36:45.297Z"
it "should resolve if the object cannot be found", ->
spyOn(DatabaseStore, 'find').andCallFake (klass, id) =>
return Promise.resolve(null)
spyOn(DatabaseStore, 'unpersistModel')
waitsForPromise =>
NylasAPI._handleDeltaDeletion(@delta)
runs =>
expect(DatabaseStore.find).toHaveBeenCalledWith(Thread, 'idhere')
expect(DatabaseStore.unpersistModel).not.toHaveBeenCalled()
it "should call unpersistModel if the object exists", ->
spyOn(DatabaseStore, 'find').andCallFake (klass, id) =>
return Promise.resolve(@thread)
spyOn(DatabaseStore, 'unpersistModel')
waitsForPromise =>
NylasAPI._handleDeltaDeletion(@delta)
runs =>
expect(DatabaseStore.find).toHaveBeenCalledWith(Thread, 'idhere')
expect(DatabaseStore.unpersistModel).toHaveBeenCalledWith(@thread)
# These specs are on hold because this function is changing very soon
xdescribe "handleModelResponse", ->
it "should reject if no JSON is provided", ->
it "should resolve if an empty JSON array is provided", ->
describe "if JSON contains the same object more than once", ->
it "should warn", ->
it "should omit duplicates", ->
describe "if JSON contains objects which are of unknown types", ->
it "should warn and resolve", ->
describe "when the object type is `thread`", ->
it "should check that models are acceptable", ->
describe "when the object type is `draft`", ->
it "should check that models are acceptable", ->
it "should call persistModels to save all of the received objects", ->
it "should resolve with the objects", ->

View file

@ -6,7 +6,6 @@ PriorityUICoordinator = require '../priority-ui-coordinator'
DatabaseStore = require './stores/database-store'
NylasSyncWorker = require './nylas-sync-worker'
NylasLongConnection = require './nylas-long-connection'
DatabaseObjectRegistry = require '../database-object-registry'
async = require 'async'
PermanentErrorCodes = [400, 404, 500]
@ -236,12 +235,13 @@ class NylasAPI
{pathname, query} = url.parse(modelUrl, true)
components = pathname.split('/')
if components.length is 5
[root, ns, nsId, collection, klassId] = components
klass = DatabaseObjectRegistry.get(collection[0..-2]) # Warning: threads => thread
if components.length is 3
[root, collection, klassId] = components
klass = @_apiObjectToClassMap[collection[0..-2]] # Warning: threads => thread
if klass and klassId and klassId.length > 0
console.warn("Deleting #{klass.name}:#{klassId} due to API 404")
unless atom.inSpecMode()
console.warn("Deleting #{klass.name}:#{klassId} due to API 404")
DatabaseStore.find(klass, klassId).then (model) ->
if model
return DatabaseStore.unpersistModel(model)
@ -279,6 +279,29 @@ class NylasAPI
if delta.attributes
Object.defineProperty(delta.attributes, '_delta', { get: -> delta })
{create, modify, destroy} = @_clusterDeltas(deltas)
# Apply all the deltas to create objects. Gets promises for handling
# each type of model in the `create` hash, waits for them all to resolve.
create[type] = @_handleModelResponse(_.values(dict)) for type, dict of create
Promise.props(create).then (created) =>
# Apply all the deltas to modify objects. Gets promises for handling
# each type of model in the `modify` hash, waits for them all to resolve.
modify[type] = @_handleModelResponse(_.values(dict)) for type, dict of modify
Promise.props(modify).then (modified) =>
# Now that we've persisted creates/updates, fire an action
# that allows other parts of the app to update based on new models
# (notifications)
if _.flatten(_.values(created)).length > 0
Actions.didPassivelyReceiveNewModels(created)
# Apply all of the deletions
destroyPromises = destroy.map(@_handleDeltaDeletion)
Promise.settle(destroyPromises).then =>
Actions.longPollProcessedDeltas()
_clusterDeltas: (deltas) ->
# Group deltas by object type so we can mutate the cache efficiently.
# NOTE: This code must not just accumulate creates, modifies and destroys
# but also de-dupe them. We cannot call "persistModels(itemA, itemA, itemB)"
@ -297,32 +320,14 @@ class NylasAPI
else if delta.event is 'delete'
destroy.push(delta)
# Apply all the deltas to create objects. Gets promises for handling
# each type of model in the `create` hash, waits for them all to resolve.
create[type] = @_handleModelResponse(_.values(dict)) for type, dict of create
Promise.props(create).then (created) =>
# Apply all the deltas to modify objects. Gets promises for handling
# each type of model in the `modify` hash, waits for them all to resolve.
modify[type] = @_handleModelResponse(_.values(dict)) for type, dict of modify
Promise.props(modify).then (modified) ->
{create, modify, destroy}
# Now that we've persisted creates/updates, fire an action
# that allows other parts of the app to update based on new models
# (notifications)
if _.flatten(_.values(created)).length > 0
Actions.didPassivelyReceiveNewModels(created)
# Apply all of the deletions
destroyPromises = destroy.map (delta) ->
console.log(" - 1 #{delta.object} (#{delta.id})")
klass = DatabaseObjectRegistry.get(delta.object)
return unless klass
DatabaseStore.find(klass, delta.id).then (model) ->
return Promise.resolve() unless model
return DatabaseStore.unpersistModel(model)
Promise.settle(destroyPromises).then =>
Actions.longPollProcessedDeltas()
_handleDeltaDeletion: (delta) ->
klass = @_apiObjectToClassMap[delta.object]
return unless klass
DatabaseStore.find(klass, delta.id).then (model) ->
return Promise.resolve() unless model
return DatabaseStore.unpersistModel(model)
# Returns a Promsie that resolves when any parsed out models (if any)
# have been created and persisted to the database.
@ -340,38 +345,33 @@ class NylasAPI
console.warn("NylasAPI.handleModelResponse: called with non-unique object set. Maybe an API request returned the same object more than once?")
type = jsons[0].object
name = @_apiObjectToClassnameMap[type]
if not name
klass = @_apiObjectToClassMap[type]
if not klass
console.warn("NylasAPI::handleModelResponse: Received unknown API object type: #{type}")
return Promise.resolve([])
accepted = Promise.resolve(uniquedJSONs)
if type is "thread"
Thread = require './models/thread'
accepted = @_acceptableModelsInResponse(Thread, uniquedJSONs)
else if type is "draft"
Message = require './models/message'
accepted = @_acceptableModelsInResponse(Message, uniquedJSONs)
if type is "thread" or type is "draft"
accepted = @_acceptableModelsInResponse(klass, uniquedJSONs)
mapper = (json) ->
return DatabaseObjectRegistry.deserialize(name, json)
mapper = (json) -> (new klass).fromJSON(json)
accepted.map(mapper).then (objects) ->
DatabaseStore.persistModels(objects).then ->
return Promise.resolve(objects)
_apiObjectToClassnameMap:
"file": "File"
"event": "Event"
"label": "Label"
"folder": "Folder"
"thread": "Thread"
"draft": "Message"
"account": "Account"
"message": "Message"
"contact": "Contact"
"calendar": "Calendar"
"metadata": "Metadata"
_apiObjectToClassMap:
"file": require('./models/file')
"event": require('./models/event')
"label": require('./models/label')
"folder": require('./models/folder')
"thread": require('./models/thread')
"draft": require('./models/message')
"account": require('./models/account')
"message": require('./models/message')
"contact": require('./models/contact')
"calendar": require('./models/calendar')
"metadata": require('./models/metadata')
_acceptableModelsInResponse: (klass, jsons) ->
# Filter out models that are locked by pending optimistic changes