From c57e74b7e2b0807696924a9dd5fcff6f3b4339a5 Mon Sep 17 00:00:00 2001 From: Mostafa Rashed <17770919+mrashed-dev@users.noreply.github.com> Date: Wed, 18 Aug 2021 17:48:50 -0400 Subject: [PATCH 1/5] Initial commit of refactor Created two new "base" classes called `model` and `model-collection`. These two classes provide a way for creating models that are not the typical Nylas API models (Event, Calendar, etc.) but will still require similar functionality. Also not all models are "RESTful", `Contact.Email` for example does not. --- src/models/attributes.ts | 22 +- .../contact-restful-model-collection.ts | 2 +- src/models/contact.ts | 19 +- src/models/event-conferencing.ts | 10 +- src/models/event-participant.ts | 6 +- src/models/management-model-collection.ts | 2 +- src/models/model-collection.ts | 255 ++++++++++++++++++ src/models/model.ts | 44 +++ src/models/neural-categorizer.ts | 5 +- src/models/neural-signature-contact.ts | 29 +- src/models/restful-model-collection.ts | 243 +---------------- src/models/restful-model-instance.ts | 4 +- src/models/restful-model.ts | 31 +-- 13 files changed, 362 insertions(+), 310 deletions(-) create mode 100644 src/models/model-collection.ts create mode 100644 src/models/model.ts diff --git a/src/models/attributes.ts b/src/models/attributes.ts index f859a450..1a12660e 100644 --- a/src/models/attributes.ts +++ b/src/models/attributes.ts @@ -1,3 +1,4 @@ +import Model from './model'; import RestfulModel from './restful-model'; // The Attribute class represents a single model attribute, like 'namespace_id' @@ -33,7 +34,7 @@ export class Attribute { } class AttributeObject extends Attribute { - itemClass?: typeof RestfulModel; + itemClass?: typeof Model | typeof RestfulModel; constructor({ modelKey, @@ -43,7 +44,7 @@ class AttributeObject extends Attribute { }: { modelKey: string; jsonKey?: string; - itemClass?: typeof RestfulModel; + itemClass?: typeof Model | typeof RestfulModel; readOnly?: boolean; }) { super({ modelKey, jsonKey, readOnly }); @@ -64,7 +65,11 @@ class AttributeObject extends Attribute { if (!val || !this.itemClass) { return val; } - return new this.itemClass(_parent.connection, val); + + if (this.itemClass.prototype instanceof RestfulModel) { + return new this.itemClass(_parent.connection, val); + } + return new this.itemClass(val); } } @@ -155,7 +160,7 @@ class AttributeDateTime extends Attribute { } class AttributeCollection extends Attribute { - itemClass: typeof RestfulModel; + itemClass: typeof Model | typeof RestfulModel; constructor({ modelKey, @@ -165,7 +170,7 @@ class AttributeCollection extends Attribute { }: { modelKey: string; jsonKey?: string; - itemClass: typeof RestfulModel; + itemClass: typeof Model | typeof RestfulModel; readOnly?: boolean; }) { super({ modelKey, jsonKey, readOnly }); @@ -193,7 +198,12 @@ class AttributeCollection extends Attribute { } const objs = []; for (const objJSON of json) { - const obj = new this.itemClass(_parent.connection, objJSON); + let obj; + if (this.itemClass.prototype instanceof RestfulModel) { + obj = new this.itemClass(_parent.connection, objJSON); + } else { + obj = new this.itemClass(objJSON); + } objs.push(obj); } return objs; diff --git a/src/models/contact-restful-model-collection.ts b/src/models/contact-restful-model-collection.ts index 7c395721..de496fd5 100644 --- a/src/models/contact-restful-model-collection.ts +++ b/src/models/contact-restful-model-collection.ts @@ -24,7 +24,7 @@ export default class ContactRestfulModelCollection extends RestfulModelCollectio }) .then(json => { const groups = json.map((group: { [key: string]: any }) => { - return new Group(this.connection, group); + return new Group(group); }); if (callback) { callback(null, groups); diff --git a/src/models/contact.ts b/src/models/contact.ts index 74308284..1c28dc6f 100644 --- a/src/models/contact.ts +++ b/src/models/contact.ts @@ -1,7 +1,8 @@ import RestfulModel, { SaveCallback } from './restful-model'; import Attributes from './attributes'; +import Model from './model'; -export class EmailAddress extends RestfulModel { +export class EmailAddress extends Model { type?: string; email?: string; @@ -13,7 +14,6 @@ export class EmailAddress extends RestfulModel { } } -EmailAddress.collectionName = 'email_addresses'; EmailAddress.attributes = { ...RestfulModel.attributes, type: Attributes.String({ @@ -24,7 +24,7 @@ EmailAddress.attributes = { }), }; -class IMAddress extends RestfulModel { +class IMAddress extends Model { type?: string; imAddress?: string; @@ -36,7 +36,6 @@ class IMAddress extends RestfulModel { } } -IMAddress.collectionName = 'im_addresses'; IMAddress.attributes = { ...RestfulModel.attributes, type: Attributes.String({ @@ -48,7 +47,7 @@ IMAddress.attributes = { }), }; -class PhysicalAddress extends RestfulModel { +class PhysicalAddress extends Model { type?: string; format?: string; address?: string; @@ -76,7 +75,6 @@ class PhysicalAddress extends RestfulModel { } } -PhysicalAddress.collectionName = 'physical_addresses'; PhysicalAddress.attributes = { ...RestfulModel.attributes, type: Attributes.String({ @@ -107,7 +105,7 @@ PhysicalAddress.attributes = { }), }; -export class PhoneNumber extends RestfulModel { +export class PhoneNumber extends Model { type?: string; number?: string; @@ -119,7 +117,6 @@ export class PhoneNumber extends RestfulModel { } } -PhoneNumber.collectionName = 'phone_numbers'; PhoneNumber.attributes = { ...RestfulModel.attributes, type: Attributes.String({ @@ -130,7 +127,7 @@ PhoneNumber.attributes = { }), }; -export class WebPage extends RestfulModel { +export class WebPage extends Model { type?: string; url?: string; @@ -143,7 +140,6 @@ export class WebPage extends RestfulModel { } } -WebPage.collectionName = 'web_pages'; WebPage.attributes = { ...RestfulModel.attributes, type: Attributes.String({ @@ -154,12 +150,11 @@ WebPage.attributes = { }), }; -export class Group extends RestfulModel { +export class Group extends Model { type?: string; path?: string; } -Group.collectionName = 'groups'; Group.attributes = { ...RestfulModel.attributes, name: Attributes.String({ diff --git a/src/models/event-conferencing.ts b/src/models/event-conferencing.ts index c87470b6..4b91d8a1 100644 --- a/src/models/event-conferencing.ts +++ b/src/models/event-conferencing.ts @@ -1,7 +1,7 @@ -import RestfulModel from './restful-model'; import Attributes from './attributes'; +import Model from './model'; -class EventConferencingDetails extends RestfulModel { +class EventConferencingDetails extends Model { meetingCode?: string; phone?: string[]; password?: string; @@ -18,7 +18,7 @@ class EventConferencingDetails extends RestfulModel { }; } } -EventConferencingDetails.collectionName = 'event_conferencing_details'; + EventConferencingDetails.attributes = { meetingCode: Attributes.String({ modelKey: 'meetingCode', @@ -38,7 +38,7 @@ EventConferencingDetails.attributes = { }), }; -export class EventConferencing extends RestfulModel { +export class EventConferencing extends Model { details?: EventConferencingDetails; provider?: string; autocreate?: { @@ -53,7 +53,7 @@ export class EventConferencing extends RestfulModel { }; } } -EventConferencing.collectionName = 'event_conferencing'; + EventConferencing.attributes = { details: Attributes.Object({ modelKey: 'details', diff --git a/src/models/event-participant.ts b/src/models/event-participant.ts index 0ba4377f..b300f57f 100644 --- a/src/models/event-participant.ts +++ b/src/models/event-participant.ts @@ -1,7 +1,7 @@ -import RestfulModel from './restful-model'; import Attributes from './attributes'; +import Model from './model'; -export default class EventParticipant extends RestfulModel { +export default class EventParticipant extends Model { name?: string; email?: string; status?: string; @@ -14,7 +14,7 @@ export default class EventParticipant extends RestfulModel { return json; } } -EventParticipant.collectionName = 'event-participants'; + EventParticipant.attributes = { name: Attributes.String({ modelKey: 'name', diff --git a/src/models/management-model-collection.ts b/src/models/management-model-collection.ts index e98f1623..8f68a2d7 100644 --- a/src/models/management-model-collection.ts +++ b/src/models/management-model-collection.ts @@ -16,7 +16,7 @@ export default class ManagementModelCollection< this.clientId = clientId; } - path() { + get path(): string { return `/a/${this.clientId}/${this.modelClass.collectionName}`; } diff --git a/src/models/model-collection.ts b/src/models/model-collection.ts new file mode 100644 index 00000000..53c41d27 --- /dev/null +++ b/src/models/model-collection.ts @@ -0,0 +1,255 @@ +import NylasConnection from '../nylas-connection'; +import Model from './model'; + +export type GetCallback = (error: Error | null, result?: Model) => void; + +const REQUEST_CHUNK_SIZE = 100; + +export default class ModelCollection { + connection: NylasConnection; + modelClass: typeof Model; + private readonly _path: string; + + constructor( + modelClass: typeof Model, + connection: NylasConnection, + path: string + ) { + this.modelClass = modelClass; + this.connection = connection; + this._path = path; + if (!this.connection) { + throw new Error('Connection object not provided'); + } + if (!this.modelClass) { + throw new Error('Model class not provided'); + } + } + + get path(): string { + return this._path; + } + + forEach( + params: { [key: string]: any } = {}, + eachCallback: (item: T) => void, + completeCallback?: (err?: Error | null | undefined) => void + ) { + if (params.view == 'count') { + const err = new Error('forEach() cannot be called with the count view'); + if (completeCallback) { + completeCallback(err); + } + return Promise.reject(err); + } + + let offset = 0; + + const iteratee = (): Promise => { + return this._getItems(params, offset, REQUEST_CHUNK_SIZE).then(items => { + for (const item of items) { + eachCallback(item); + } + offset += items.length; + const finished = items.length < REQUEST_CHUNK_SIZE; + if (!finished) { + return iteratee(); + } + }); + }; + + iteratee().then( + () => { + if (completeCallback) { + completeCallback(); + } + }, + (err: Error) => { + if (completeCallback) { + return completeCallback(err); + } + } + ); + } + + list( + params: { [key: string]: any } = {}, + callback?: (error: Error | null, obj?: T[]) => void + ) { + if (params.view == 'count') { + const err = new Error('list() cannot be called with the count view'); + if (callback) { + callback(err); + } + return Promise.reject(err); + } + + const limit = params.limit || Infinity; + const offset = params.offset; + return this._range({ params, offset, limit, callback }); + } + + find( + id: string, + paramsArg?: { [key: string]: any } | GetCallback | null, + callbackArg?: GetCallback | { [key: string]: any } | null + ) { + // callback used to be the second argument, and params was the third + let callback: GetCallback | undefined; + if (typeof callbackArg === 'function') { + callback = callbackArg as GetCallback; + } else if (typeof paramsArg === 'function') { + callback = paramsArg as GetCallback; + } + + let params: { [key: string]: any } = {}; + if (paramsArg && typeof paramsArg === 'object') { + params = paramsArg; + } else if (callbackArg && typeof callbackArg === 'object') { + params = callbackArg; + } + + if (!id) { + const err = new Error('find() must be called with an item id'); + if (callback) { + callback(err); + } + return Promise.reject(err); + } + + if (params.view == 'count' || params.view == 'ids') { + const err = new Error( + 'find() cannot be called with the count or ids view' + ); + if (callback) { + callback(err); + } + return Promise.reject(err); + } + + return this._getModel(id, params) + .then(model => { + if (callback) { + callback(null, model); + } + return Promise.resolve(model); + }) + .catch(err => { + if (callback) { + callback(err); + } + return Promise.reject(err); + }); + } + + _range({ + params = {}, + offset = 0, + limit = 100, + callback, + path, + }: { + params?: { [key: string]: any }; + offset?: number; + limit?: number; + callback?: (error: Error | null, results?: T[]) => void; + path?: string; + }) { + let accumulated: T[] = []; + + const iteratee = (): Promise => { + const chunkOffset = offset + accumulated.length; + const chunkLimit = Math.min( + REQUEST_CHUNK_SIZE, + limit - accumulated.length + ); + return this._getItems(params, chunkOffset, chunkLimit, path).then( + items => { + accumulated = accumulated.concat(items); + const finished = + items.length < REQUEST_CHUNK_SIZE || accumulated.length >= limit; + if (!finished) { + return iteratee(); + } + } + ); + }; + + // do not return rejected promise when callback is provided + // to prevent unhandled rejection warning + return iteratee().then( + () => { + if (callback) { + return callback(null, accumulated); + } + return accumulated; + }, + (err: Error) => { + if (callback) { + return callback(err); + } + throw err; + } + ); + } + + _getItems( + params: { [key: string]: any }, + offset: number, + limit: number, + path?: string + ): Promise { + // Items can be either models or ids + + if (!path) { + path = this.path; + } + + if (params.view == 'ids') { + return this.connection.request({ + method: 'GET', + path, + qs: { ...params, offset, limit }, + }); + } + + return this._getModelCollection(params, offset, limit, path); + } + + _createModel(json: { [key: string]: any }): T { + return new this.modelClass(json) as T; + } + + _getModel(id: string, params: { [key: string]: any } = {}): Promise { + return this.connection + .request({ + method: 'GET', + path: `${this.path}/${id}`, + qs: params, + }) + .then((json: any) => { + const model = this._createModel(json); + return Promise.resolve(model); + }); + } + + _getModelCollection( + params: { [key: string]: any }, + offset: number, + limit: number, + path: string + ): Promise { + return this.connection + .request({ + method: 'GET', + path, + qs: { ...params, offset, limit }, + }) + .then((jsonArray: any) => { + const models = jsonArray.map((json: any) => { + return this._createModel(json); + }); + return Promise.resolve(models); + }); + } +} diff --git a/src/models/model.ts b/src/models/model.ts new file mode 100644 index 00000000..b5d31d68 --- /dev/null +++ b/src/models/model.ts @@ -0,0 +1,44 @@ +import { Attribute } from './attributes'; + +export type SaveCallback = (error: Error | null, result?: any) => void; + +export default class Model { + static attributes: { [key: string]: Attribute }; + + constructor(json?: any) { + if (json) { + this.fromJSON(json); + } + } + + attributes(): { [key: string]: Attribute } { + return (this.constructor as any).attributes; + } + + fromJSON(json?: any): Model { + const attributes = this.attributes(); + for (const attrName in attributes) { + const attr = attributes[attrName]; + if (json[attr.jsonKey] !== undefined) { + (this as any)[attrName] = attr.fromJSON(json[attr.jsonKey], this); + } + } + return this; + } + + toJSON(enforceReadOnly?: boolean): any { + const json: any = {}; + const attributes = this.attributes(); + for (const attrName in attributes) { + if (!enforceReadOnly || !attributes[attrName].readOnly) { + const attr = attributes[attrName]; + json[attr.jsonKey] = attr.toJSON((this as any)[attrName]); + } + } + return json; + } + + toString(): string { + return JSON.stringify(this.toJSON()); + } +} diff --git a/src/models/neural-categorizer.ts b/src/models/neural-categorizer.ts index b0809c49..43be1174 100644 --- a/src/models/neural-categorizer.ts +++ b/src/models/neural-categorizer.ts @@ -1,8 +1,8 @@ import Attributes from './attributes'; import Message from './message'; -import RestfulModel from './restful-model'; +import Model from './model'; -export class Categorize extends RestfulModel { +export class Categorize extends Model { category?: string; categorizedAt?: Date; modelVersion?: string; @@ -18,7 +18,6 @@ export class Categorize extends RestfulModel { } } -Categorize.collectionName = 'categorize'; Categorize.attributes = { category: Attributes.String({ modelKey: 'category', diff --git a/src/models/neural-signature-contact.ts b/src/models/neural-signature-contact.ts index d80059ac..2ba2dab2 100644 --- a/src/models/neural-signature-contact.ts +++ b/src/models/neural-signature-contact.ts @@ -1,8 +1,9 @@ -import RestfulModel from './restful-model'; import Attributes from './attributes'; import { Contact, EmailAddress, PhoneNumber, WebPage } from './contact'; +import Model from './model'; +import NylasConnection from '../nylas-connection'; -class Link extends RestfulModel { +class Link extends Model { description?: string; url?: string; @@ -14,7 +15,6 @@ class Link extends RestfulModel { } } -Link.collectionName = 'link'; Link.attributes = { description: Attributes.String({ modelKey: 'description', @@ -24,7 +24,7 @@ Link.attributes = { }), }; -class Name extends RestfulModel { +class Name extends Model { firstName?: string; lastName?: string; @@ -36,7 +36,6 @@ class Name extends RestfulModel { } } -Name.collectionName = 'name'; Name.attributes = { firstName: Attributes.String({ modelKey: 'firstName', @@ -48,7 +47,7 @@ Name.attributes = { }), }; -export default class NeuralSignatureContact extends RestfulModel { +export default class NeuralSignatureContact extends Model { jobTitles?: string[]; links?: Link[]; phoneNumbers?: string[]; @@ -65,8 +64,9 @@ export default class NeuralSignatureContact extends RestfulModel { }; } - toContactObject(): Contact { - const contact = new Contact(this.connection); + //TODO::Perhaps this gets moved out to Neural? + toContactObject(connection: NylasConnection): Contact { + const contact = new Contact(connection); if (this.names) { contact.givenName = this.names[0].firstName; @@ -78,18 +78,14 @@ export default class NeuralSignatureContact extends RestfulModel { if (this.emails) { const contactEmails: EmailAddress[] = []; this.emails.forEach(email => - contactEmails.push( - new EmailAddress(this.connection, { type: 'personal', email: email }) - ) + contactEmails.push(new EmailAddress({ type: 'personal', email: email })) ); contact.emailAddresses = contactEmails; } if (this.phoneNumbers) { const contactNumbers: PhoneNumber[] = []; this.phoneNumbers.forEach(number => - contactNumbers.push( - new PhoneNumber(this.connection, { type: 'mobile', number: number }) - ) + contactNumbers.push(new PhoneNumber({ type: 'mobile', number: number })) ); contact.phoneNumbers = contactNumbers; } @@ -97,9 +93,7 @@ export default class NeuralSignatureContact extends RestfulModel { const webPages: WebPage[] = []; this.links.forEach(link => { if (link['url']) { - webPages.push( - new WebPage(this.connection, { type: 'homepage', url: link['url'] }) - ); + webPages.push(new WebPage({ type: 'homepage', url: link['url'] })); } }); contact.webPages = webPages; @@ -109,7 +103,6 @@ export default class NeuralSignatureContact extends RestfulModel { } } -NeuralSignatureContact.collectionName = 'signature_contact'; NeuralSignatureContact.attributes = { jobTitles: Attributes.StringList({ modelKey: 'jobTitles', diff --git a/src/models/restful-model-collection.ts b/src/models/restful-model-collection.ts index cd7079ee..25f942c0 100644 --- a/src/models/restful-model-collection.ts +++ b/src/models/restful-model-collection.ts @@ -2,66 +2,18 @@ import Message from './message'; import NylasConnection from '../nylas-connection'; import RestfulModel from './restful-model'; import Thread from './thread'; - -const REQUEST_CHUNK_SIZE = 100; +import ModelCollection from './model-collection'; export type GetCallback = (error: Error | null, result?: RestfulModel) => void; -export default class RestfulModelCollection { - connection: NylasConnection; +export default class RestfulModelCollection< + T extends RestfulModel +> extends ModelCollection { modelClass: typeof RestfulModel; constructor(modelClass: typeof RestfulModel, connection: NylasConnection) { + super(modelClass, connection, modelClass.collectionName); this.modelClass = modelClass; - this.connection = connection; - if (!(this.connection instanceof NylasConnection)) { - throw new Error('Connection object not provided'); - } - if (!this.modelClass) { - throw new Error('Model class not provided'); - } - } - - forEach( - params: { [key: string]: any } = {}, - eachCallback: (item: T) => void, - completeCallback?: (err?: Error | null | undefined) => void - ) { - if (params.view == 'count') { - const err = new Error('forEach() cannot be called with the count view'); - if (completeCallback) { - completeCallback(err); - } - return Promise.reject(err); - } - - let offset = 0; - - const iteratee = (): Promise => { - return this._getItems(params, offset, REQUEST_CHUNK_SIZE).then(items => { - for (const item of items) { - eachCallback(item); - } - offset += items.length; - const finished = items.length < REQUEST_CHUNK_SIZE; - if (finished === false) { - return iteratee(); - } - }); - }; - - iteratee().then( - () => { - if (completeCallback) { - completeCallback(); - } - }, - (err: Error) => { - if (completeCallback) { - return completeCallback(err); - } - } - ); } count( @@ -71,7 +23,7 @@ export default class RestfulModelCollection { return this.connection .request({ method: 'GET', - path: this.path(), + path: this.path, qs: { view: 'count', ...params }, }) .then((json: any) => { @@ -115,76 +67,6 @@ export default class RestfulModelCollection { }); } - list( - params: { [key: string]: any } = {}, - callback?: (error: Error | null, obj?: T[]) => void - ) { - if (params.view == 'count') { - const err = new Error('list() cannot be called with the count view'); - if (callback) { - callback(err); - } - return Promise.reject(err); - } - - const limit = params.limit || Infinity; - const offset = params.offset; - return this._range({ params, offset, limit, callback }); - } - - find( - id: string, - paramsArg?: { [key: string]: any } | GetCallback | null, - callbackArg?: GetCallback | { [key: string]: any } | null - ) { - // callback used to be the second argument, and params was the third - let callback: GetCallback | undefined; - if (typeof callbackArg === 'function') { - callback = callbackArg as GetCallback; - } else if (typeof paramsArg === 'function') { - callback = paramsArg as GetCallback; - } - - let params: { [key: string]: any } = {}; - if (paramsArg && typeof paramsArg === 'object') { - params = paramsArg; - } else if (callbackArg && typeof callbackArg === 'object') { - params = callbackArg; - } - - if (!id) { - const err = new Error('find() must be called with an item id'); - if (callback) { - callback(err); - } - return Promise.reject(err); - } - - if (params.view == 'count' || params.view == 'ids') { - const err = new Error( - 'find() cannot be called with the count or ids view' - ); - if (callback) { - callback(err); - } - return Promise.reject(err); - } - - return this._getModel(id, params) - .then(model => { - if (callback) { - callback(null, model); - } - return Promise.resolve(model); - }) - .catch(err => { - if (callback) { - callback(err); - } - return Promise.reject(err); - }); - } - search( query: string, params: { [key: string]: any } = {}, @@ -211,7 +93,7 @@ export default class RestfulModelCollection { params.q = query; const limit = params.limit || 40; const offset = params.offset; - const path = `${this.path()}/search`; + const path = `${this.path}/search`; return this._range({ params, offset, limit, path }); } @@ -262,7 +144,7 @@ export default class RestfulModelCollection { method: 'DELETE', qs: qs, body: body, - path: `${this.path()}/${item.id}`, + path: `${this.path}/${item.id}`, }) .then(data => { if (callback) { @@ -287,118 +169,11 @@ export default class RestfulModelCollection { return model; } - path() { + get path(): string { return `/${this.modelClass.collectionName}`; } - _range({ - params = {}, - offset = 0, - limit = 100, - callback, - path, - }: { - params?: { [key: string]: any }; - offset?: number; - limit?: number; - callback?: (error: Error | null, results?: T[]) => void; - path?: string; - }) { - let accumulated: T[] = []; - - const iteratee = (): Promise => { - const chunkOffset = offset + accumulated.length; - const chunkLimit = Math.min( - REQUEST_CHUNK_SIZE, - limit - accumulated.length - ); - return this._getItems(params, chunkOffset, chunkLimit, path).then( - items => { - accumulated = accumulated.concat(items); - const finished = - items.length < REQUEST_CHUNK_SIZE || accumulated.length >= limit; - if (finished === false) { - return iteratee(); - } - } - ); - }; - - // do not return rejected promise when callback is provided - // to prevent unhandled rejection warning - return iteratee().then( - () => { - if (callback) { - return callback(null, accumulated); - } - return accumulated; - }, - (err: Error) => { - if (callback) { - return callback(err); - } - throw err; - } - ); - } - - _getItems( - params: { [key: string]: any }, - offset: number, - limit: number, - path?: string - ): Promise { - // Items can be either models or ids - - if (!path) { - path = this.path(); - } - - if (params.view == 'ids') { - return this.connection.request({ - method: 'GET', - path, - qs: { ...params, offset, limit }, - }); - } - - return this._getModelCollection(params, offset, limit, path); - } - _createModel(json: { [key: string]: any }) { return new this.modelClass(this.connection, json) as T; } - - _getModel(id: string, params: { [key: string]: any } = {}): Promise { - return this.connection - .request({ - method: 'GET', - path: `${this.path()}/${id}`, - qs: params, - }) - .then((json: any) => { - const model = this._createModel(json); - return Promise.resolve(model); - }); - } - - _getModelCollection( - params: { [key: string]: any }, - offset: number, - limit: number, - path: string - ): Promise { - return this.connection - .request({ - method: 'GET', - path, - qs: { ...params, offset, limit }, - }) - .then((jsonArray: any) => { - const models = jsonArray.map((json: any) => { - return this._createModel(json); - }); - return Promise.resolve(models); - }); - } } diff --git a/src/models/restful-model-instance.ts b/src/models/restful-model-instance.ts index 2fb93ac8..4fa52c91 100644 --- a/src/models/restful-model-instance.ts +++ b/src/models/restful-model-instance.ts @@ -16,7 +16,7 @@ export default class RestfulModelInstance { } } - path() { + get path(): string { return `/${this.modelClass.endpointName}`; } @@ -24,7 +24,7 @@ export default class RestfulModelInstance { return this.connection .request({ method: 'GET', - path: this.path(), + path: this.path, qs: params, }) .then(json => { diff --git a/src/models/restful-model.ts b/src/models/restful-model.ts index 36d77c77..6e9c59c8 100644 --- a/src/models/restful-model.ts +++ b/src/models/restful-model.ts @@ -1,5 +1,6 @@ -import Attributes, { Attribute } from './attributes'; +import Attributes from './attributes'; import NylasConnection from '../nylas-connection'; +import Model from './model'; export type SaveCallback = (error: Error | null, result?: RestfulModel) => void; @@ -10,10 +11,9 @@ interface RestfulModelJSON { [key: string]: any; } -export default class RestfulModel { +export default class RestfulModel extends Model { static endpointName = ''; // overrridden in subclasses static collectionName = ''; // overrridden in subclasses - static attributes: { [key: string]: Attribute }; accountId?: string; connection: NylasConnection; @@ -21,8 +21,9 @@ export default class RestfulModel { object?: string; constructor(connection: NylasConnection, json?: Partial) { + super(); this.connection = connection; - if (!(this.connection instanceof NylasConnection)) { + if (!this.connection) { throw new Error('Connection object not provided'); } if (json) { @@ -30,10 +31,6 @@ export default class RestfulModel { } } - attributes(): { [key: string]: Attribute } { - return (this.constructor as any).attributes; - } - isEqual(other: RestfulModel) { return ( (other ? other.id : undefined) === this.id && @@ -41,7 +38,7 @@ export default class RestfulModel { ); } - fromJSON(json: Partial = {}) { + fromJSON(json: Partial = {}): RestfulModel { const attributes = this.attributes(); for (const attrName in attributes) { const attr = attributes[attrName]; @@ -52,18 +49,6 @@ export default class RestfulModel { return this; } - toJSON(enforceReadOnly?: boolean) { - const json: any = {}; - const attributes = this.attributes(); - for (const attrName in attributes) { - if (!enforceReadOnly || !attributes[attrName].readOnly) { - const attr = attributes[attrName]; - json[attr.jsonKey] = attr.toJSON((this as any)[attrName]); - } - } - return json; - } - // Subclasses should override this method. pathPrefix() { return ''; @@ -97,10 +82,6 @@ export default class RestfulModel { }; } - toString() { - return JSON.stringify(this.toJSON()); - } - // Not every model needs to have a save function, but those who // do shouldn't have to reimplement the same boilerplate. // They should instead define a save() function which calls _save. From 70c195484d3e5307dbbd9e37109e5e626baf44b2 Mon Sep 17 00:00:00 2001 From: Mostafa Rashed <17770919+mrashed-dev@users.noreply.github.com> Date: Wed, 18 Aug 2021 17:49:08 -0400 Subject: [PATCH 2/5] Only a couple of test case fixes --- __tests__/neural-spec.js | 4 +++- __tests__/restful-model-collection-spec.js | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/__tests__/neural-spec.js b/__tests__/neural-spec.js index 3d2f33dd..592cc491 100644 --- a/__tests__/neural-spec.js +++ b/__tests__/neural-spec.js @@ -235,7 +235,9 @@ describe('Neural', () => { return testContext.connection.neural .extractSignature(['abc123']) .then(sigList => { - const contact = sigList[0].contacts.toContactObject(); + const contact = sigList[0].contacts.toContactObject( + testContext.connection + ); expect(contact.givenName).toEqual('Nylas'); expect(contact.surname).toEqual('Swag'); diff --git a/__tests__/restful-model-collection-spec.js b/__tests__/restful-model-collection-spec.js index b677df23..3a46851a 100644 --- a/__tests__/restful-model-collection-spec.js +++ b/__tests__/restful-model-collection-spec.js @@ -714,7 +714,7 @@ describe('RestfulModelCollection', () => { describe('path', () => { test("should return the modelClass' collectionName with no prefix", () => { - expect(testContext.collection.path()).toEqual('/threads'); + expect(testContext.collection.path).toEqual('/threads'); }); }); From 5e2425e3090263ff8c9ae1a00578369d7778641a Mon Sep 17 00:00:00 2001 From: Mostafa Rashed <17770919+mrashed-dev@users.noreply.github.com> Date: Fri, 20 Aug 2021 12:57:44 -0400 Subject: [PATCH 3/5] Update typing for model and collection related classes --- src/models/model-collection.ts | 14 +++++++------- src/models/restful-model-collection.ts | 14 +++++++------- src/models/restful-model.ts | 18 +++++++++--------- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/models/model-collection.ts b/src/models/model-collection.ts index 53c41d27..6b0e1b4b 100644 --- a/src/models/model-collection.ts +++ b/src/models/model-collection.ts @@ -34,13 +34,13 @@ export default class ModelCollection { params: { [key: string]: any } = {}, eachCallback: (item: T) => void, completeCallback?: (err?: Error | null | undefined) => void - ) { + ): void { if (params.view == 'count') { const err = new Error('forEach() cannot be called with the count view'); if (completeCallback) { completeCallback(err); } - return Promise.reject(err); + throw err; } let offset = 0; @@ -75,7 +75,7 @@ export default class ModelCollection { list( params: { [key: string]: any } = {}, callback?: (error: Error | null, obj?: T[]) => void - ) { + ): Promise { if (params.view == 'count') { const err = new Error('list() cannot be called with the count view'); if (callback) { @@ -93,7 +93,7 @@ export default class ModelCollection { id: string, paramsArg?: { [key: string]: any } | GetCallback | null, callbackArg?: GetCallback | { [key: string]: any } | null - ) { + ): Promise { // callback used to be the second argument, and params was the third let callback: GetCallback | undefined; if (typeof callbackArg === 'function') { @@ -154,7 +154,7 @@ export default class ModelCollection { limit?: number; callback?: (error: Error | null, results?: T[]) => void; path?: string; - }) { + }): Promise { let accumulated: T[] = []; const iteratee = (): Promise => { @@ -180,13 +180,13 @@ export default class ModelCollection { return iteratee().then( () => { if (callback) { - return callback(null, accumulated); + callback(null, accumulated); } return accumulated; }, (err: Error) => { if (callback) { - return callback(err); + callback(err); } throw err; } diff --git a/src/models/restful-model-collection.ts b/src/models/restful-model-collection.ts index 25f942c0..d5ec4c67 100644 --- a/src/models/restful-model-collection.ts +++ b/src/models/restful-model-collection.ts @@ -19,7 +19,7 @@ export default class RestfulModelCollection< count( params: { [key: string]: any } = {}, callback?: (err: Error | null, num?: number) => void - ) { + ): Promise { return this.connection .request({ method: 'GET', @@ -43,7 +43,7 @@ export default class RestfulModelCollection< first( params: { [key: string]: any } = {}, callback?: (error: Error | null, model?: T) => void - ) { + ): Promise { if (params.view == 'count') { const err = new Error('first() cannot be called with the count view'); if (callback) { @@ -71,7 +71,7 @@ export default class RestfulModelCollection< query: string, params: { [key: string]: any } = {}, callback?: (error: Error | null) => void - ) { + ): Promise { if (this.modelClass != Message && this.modelClass != Thread) { const err = new Error( 'search() can only be called for messages and threads' @@ -102,7 +102,7 @@ export default class RestfulModelCollection< itemOrId: T | string, params: { [key: string]: any } = {}, callback?: (error: Error | null) => void - ) { + ): any { if (!itemOrId) { const err = new Error('delete() requires an item or an id'); if (callback) { @@ -128,7 +128,7 @@ export default class RestfulModelCollection< deleteItem( options: { [key: string]: any }, callbackArg?: (error: Error | null) => void - ) { + ): any { const item = options.item; // callback used to be in the options object const callback = options.callback ? options.callback : callbackArg; @@ -160,7 +160,7 @@ export default class RestfulModelCollection< }); } - build(args: { [key: string]: any }) { + build(args: { [key: string]: any }): T { const model = this._createModel({}); for (const key in args) { const val = args[key]; @@ -173,7 +173,7 @@ export default class RestfulModelCollection< return `/${this.modelClass.collectionName}`; } - _createModel(json: { [key: string]: any }) { + _createModel(json: { [key: string]: any }): T { return new this.modelClass(this.connection, json) as T; } } diff --git a/src/models/restful-model.ts b/src/models/restful-model.ts index 6e9c59c8..18c32ec7 100644 --- a/src/models/restful-model.ts +++ b/src/models/restful-model.ts @@ -31,7 +31,7 @@ export default class RestfulModel extends Model { } } - isEqual(other: RestfulModel) { + isEqual(other: RestfulModel): boolean { return ( (other ? other.id : undefined) === this.id && (other ? other.constructor : undefined) === this.constructor @@ -50,32 +50,32 @@ export default class RestfulModel extends Model { } // Subclasses should override this method. - pathPrefix() { + pathPrefix(): string { return ''; } - saveEndpoint() { + saveEndpoint(): string { const collectionName = (this.constructor as any).collectionName; return `${this.pathPrefix()}/${collectionName}`; } // saveRequestBody is used by save(). It returns a JSON dict containing only the // fields the API allows updating. Subclasses should override this method. - saveRequestBody() { + saveRequestBody(): any { return this.toJSON(true); } // deleteRequestQueryString is used by delete(). Subclasses should override this method. - deleteRequestQueryString(_params: { [key: string]: any }) { + deleteRequestQueryString(_params: { [key: string]: any }): any { return {}; } // deleteRequestBody is used by delete(). Subclasses should override this method. - deleteRequestBody(_params: { [key: string]: any }) { + deleteRequestBody(_params: { [key: string]: any }): any { return {}; } // deleteRequestOptions is used by delete(). Subclasses should override this method. - deleteRequestOptions(params: { [key: string]: any }) { + deleteRequestOptions(params: { [key: string]: any }): any { return { body: this.deleteRequestBody(params), qs: this.deleteRequestQueryString(params), @@ -85,7 +85,7 @@ export default class RestfulModel extends Model { // Not every model needs to have a save function, but those who // do shouldn't have to reimplement the same boilerplate. // They should instead define a save() function which calls _save. - _save(params: {} | SaveCallback = {}, callback?: SaveCallback) { + _save(params: {} | SaveCallback = {}, callback?: SaveCallback): Promise { if (typeof params === 'function') { callback = params as SaveCallback; params = {}; @@ -118,7 +118,7 @@ export default class RestfulModel extends Model { params: { [key: string]: any } = {}, callback?: (error: Error | null, result?: any) => void, pathSuffix = '' - ) { + ): Promise { const collectionName = (this.constructor as any).collectionName; return this.connection .request({ From 77d7945cfe9c6837e5a8a5abdaaf8609dc3cfb9c Mon Sep 17 00:00:00 2001 From: Mostafa Rashed <17770919+mrashed-dev@users.noreply.github.com> Date: Fri, 20 Aug 2021 14:44:08 -0400 Subject: [PATCH 4/5] Remove private keyword for path --- src/models/management-model-collection.ts | 6 ++---- src/models/model-collection.ts | 8 ++------ src/models/restful-model-collection.ts | 5 +---- src/models/restful-model-instance.ts | 4 ++-- 4 files changed, 7 insertions(+), 16 deletions(-) diff --git a/src/models/management-model-collection.ts b/src/models/management-model-collection.ts index 8f68a2d7..e03db17f 100644 --- a/src/models/management-model-collection.ts +++ b/src/models/management-model-collection.ts @@ -6,6 +6,7 @@ export default class ManagementModelCollection< T extends ManagementModel > extends RestfulModelCollection { clientId: string; + path: string; constructor( modelClass: typeof ManagementModel, @@ -14,10 +15,7 @@ export default class ManagementModelCollection< ) { super(modelClass as any, connection); this.clientId = clientId; - } - - get path(): string { - return `/a/${this.clientId}/${this.modelClass.collectionName}`; + this.path = `/a/${this.clientId}/${this.modelClass.collectionName}`; } _createModel(json: { [key: string]: any }) { diff --git a/src/models/model-collection.ts b/src/models/model-collection.ts index 6b0e1b4b..3631ed62 100644 --- a/src/models/model-collection.ts +++ b/src/models/model-collection.ts @@ -8,7 +8,7 @@ const REQUEST_CHUNK_SIZE = 100; export default class ModelCollection { connection: NylasConnection; modelClass: typeof Model; - private readonly _path: string; + path: string; constructor( modelClass: typeof Model, @@ -17,7 +17,7 @@ export default class ModelCollection { ) { this.modelClass = modelClass; this.connection = connection; - this._path = path; + this.path = path; if (!this.connection) { throw new Error('Connection object not provided'); } @@ -26,10 +26,6 @@ export default class ModelCollection { } } - get path(): string { - return this._path; - } - forEach( params: { [key: string]: any } = {}, eachCallback: (item: T) => void, diff --git a/src/models/restful-model-collection.ts b/src/models/restful-model-collection.ts index d5ec4c67..c217a822 100644 --- a/src/models/restful-model-collection.ts +++ b/src/models/restful-model-collection.ts @@ -14,6 +14,7 @@ export default class RestfulModelCollection< constructor(modelClass: typeof RestfulModel, connection: NylasConnection) { super(modelClass, connection, modelClass.collectionName); this.modelClass = modelClass; + this.path = `/${this.modelClass.collectionName}`; } count( @@ -169,10 +170,6 @@ export default class RestfulModelCollection< return model; } - get path(): string { - return `/${this.modelClass.collectionName}`; - } - _createModel(json: { [key: string]: any }): T { return new this.modelClass(this.connection, json) as T; } diff --git a/src/models/restful-model-instance.ts b/src/models/restful-model-instance.ts index 4fa52c91..8f7a464a 100644 --- a/src/models/restful-model-instance.ts +++ b/src/models/restful-model-instance.ts @@ -16,7 +16,7 @@ export default class RestfulModelInstance { } } - get path(): string { + path(): string { return `/${this.modelClass.endpointName}`; } @@ -24,7 +24,7 @@ export default class RestfulModelInstance { return this.connection .request({ method: 'GET', - path: this.path, + path: this.path(), qs: params, }) .then(json => { From 892f3b570210455c6113e737093e62edb5191124 Mon Sep 17 00:00:00 2001 From: Mostafa Rashed <17770919+mrashed-dev@users.noreply.github.com> Date: Thu, 2 Sep 2021 17:19:01 -0400 Subject: [PATCH 5/5] Applied `private` and `protected` method modifiers Instead of `_[name]` notation --- __tests__/message-spec.js | 2 +- __tests__/neural-spec.js | 12 ++--- __tests__/nylas-connection-spec.js | 6 +-- __tests__/restful-model-collection-spec.js | 52 +++++++++++----------- src/models/calendar.ts | 4 +- src/models/contact.ts | 6 +-- src/models/delta-stream.ts | 16 +++---- src/models/draft.ts | 4 +- src/models/event.ts | 4 +- src/models/folder.ts | 4 +- src/models/management-model-collection.ts | 2 +- src/models/message.ts | 4 +- src/models/model-collection.ts | 24 +++++----- src/models/neural.ts | 14 +++--- src/models/restful-model-collection.ts | 8 ++-- src/models/restful-model.ts | 6 +-- src/models/thread.ts | 4 +- src/models/webhook.ts | 4 +- src/nylas-connection.ts | 4 +- 19 files changed, 90 insertions(+), 90 deletions(-) diff --git a/__tests__/message-spec.js b/__tests__/message-spec.js index f0eab7fb..93b45153 100644 --- a/__tests__/message-spec.js +++ b/__tests__/message-spec.js @@ -128,7 +128,7 @@ describe('Message', () => { Message, testContext.connection ); - testContext.collection._getModelCollection = jest.fn(() => { + testContext.collection.getModelCollection = jest.fn(() => { return Promise.resolve([testContext.message]); }); }); diff --git a/__tests__/neural-spec.js b/__tests__/neural-spec.js index 592cc491..116f3602 100644 --- a/__tests__/neural-spec.js +++ b/__tests__/neural-spec.js @@ -54,7 +54,7 @@ describe('Neural', () => { }, ]; - testContext.connection.neural._request = jest.fn(() => + testContext.connection.neural.request = jest.fn(() => Promise.resolve(serverResponse) ); }); @@ -106,7 +106,7 @@ describe('Neural', () => { text: 'This is some text', }; - testContext.connection.neural._request = jest.fn((path, body) => { + testContext.connection.neural.request = jest.fn((path, body) => { if (body['message_id']) { return Promise.resolve([sentiment]); } @@ -127,7 +127,7 @@ describe('Neural', () => { .sentimentAnalysisMessage(['abc123']) .then(convoList => { const sentBody = - testContext.connection.neural._request.mock.calls[0][1]; + testContext.connection.neural.request.mock.calls[0][1]; expect(sentBody).toEqual({ message_id: ['abc123'] }); expect(convoList.length).toEqual(1); evaluateSentiment(convoList[0]); @@ -140,7 +140,7 @@ describe('Neural', () => { .sentimentAnalysisText('This is some text') .then(convo => { const sentBody = - testContext.connection.neural._request.mock.calls[0][1]; + testContext.connection.neural.request.mock.calls[0][1]; expect(sentBody).toEqual({ text: 'This is some text' }); evaluateSentiment(convo); done(); @@ -195,7 +195,7 @@ describe('Neural', () => { }, ]; - testContext.connection.neural._request = jest.fn(() => + testContext.connection.neural.request = jest.fn(() => Promise.resolve(serverResponse) ); }); @@ -361,7 +361,7 @@ describe('Neural', () => { size: 20, }; - testContext.connection.neural._request = jest.fn(() => + testContext.connection.neural.request = jest.fn(() => Promise.resolve(serverResponse) ); }); diff --git a/__tests__/nylas-connection-spec.js b/__tests__/nylas-connection-spec.js index 3a16f087..5aa25a2f 100644 --- a/__tests__/nylas-connection-spec.js +++ b/__tests__/nylas-connection-spec.js @@ -33,13 +33,13 @@ describe('NylasConnection', () => { }); describe('mismatched api version warnings', () => { test('should not warn if Nylas API version matches SDK supported API version', () => { - const noWarning = testContext.connection._getWarningForVersion( + const noWarning = testContext.connection.getWarningForVersion( '2.0', '2.0' ); expect(noWarning).toEqual(''); - const warnSdk = testContext.connection._getWarningForVersion( + const warnSdk = testContext.connection.getWarningForVersion( '1.0', '2.0' ); @@ -47,7 +47,7 @@ describe('NylasConnection', () => { `WARNING: SDK version may not support your Nylas API version. SDK supports version 1.0 of the API and your application is currently running on version 2.0 of the API. Please update the sdk to ensure it works properly.` ); - const warnApi = testContext.connection._getWarningForVersion( + const warnApi = testContext.connection.getWarningForVersion( '2.0', '1.0' ); diff --git a/__tests__/restful-model-collection-spec.js b/__tests__/restful-model-collection-spec.js index 3a46851a..c29f17dd 100644 --- a/__tests__/restful-model-collection-spec.js +++ b/__tests__/restful-model-collection-spec.js @@ -54,7 +54,7 @@ describe('RestfulModelCollection', () => { threadsResponses.push(response); } - testContext.collection._getModelCollection = jest.fn((params, offset) => + testContext.collection.getModelCollection = jest.fn((params, offset) => Promise.resolve(threadsResponses[offset / 100]) ); }); @@ -66,7 +66,7 @@ describe('RestfulModelCollection', () => { () => {}, () => {} ); - expect(testContext.collection._getModelCollection).toHaveBeenCalledWith( + expect(testContext.collection.getModelCollection).toHaveBeenCalledWith( params, 0, 100, @@ -81,16 +81,16 @@ describe('RestfulModelCollection', () => { () => {}, () => { expect( - testContext.collection._getModelCollection.mock.calls[0] + testContext.collection.getModelCollection.mock.calls[0] ).toEqual([{ from: 'ben@nylas.com' }, 0, 100, '/threads']); expect( - testContext.collection._getModelCollection.mock.calls[1] + testContext.collection.getModelCollection.mock.calls[1] ).toEqual([{ from: 'ben@nylas.com' }, 100, 100, '/threads']); expect( - testContext.collection._getModelCollection.mock.calls[2] + testContext.collection.getModelCollection.mock.calls[2] ).toEqual([{ from: 'ben@nylas.com' }, 200, 100, '/threads']); expect( - testContext.collection._getModelCollection.mock.calls[3] + testContext.collection.getModelCollection.mock.calls[3] ).toEqual([{ from: 'ben@nylas.com' }, 300, 100, '/threads']); done(); } @@ -202,7 +202,7 @@ describe('RestfulModelCollection', () => { }, }; testContext.items = [testContext.item]; - testContext.collection._getModelCollection = jest.fn(() => { + testContext.collection.getModelCollection = jest.fn(() => { return Promise.resolve(testContext.items); }); }); @@ -215,7 +215,7 @@ describe('RestfulModelCollection', () => { }) .then(msg => { expect( - testContext.collection._getModelCollection + testContext.collection.getModelCollection ).toHaveBeenCalledWith( { from: 'ben@nylas.com', view: 'expanded' }, 0, @@ -253,7 +253,7 @@ describe('RestfulModelCollection', () => { describe('when the request fails', () => { beforeEach(() => { testContext.error = new Error('Network error'); - testContext.collection._getModelCollection = jest.fn(() => { + testContext.collection.getModelCollection = jest.fn(() => { return Promise.reject(testContext.error); }); }); @@ -278,12 +278,12 @@ describe('RestfulModelCollection', () => { describe('list', () => { test('should call range() with an infinite range', () => { - testContext.collection._range = jest.fn(); + testContext.collection.range = jest.fn(); const params = { from: 'ben@nylas.com' }; const callback = () => {}; testContext.collection.list(params, callback); - expect(testContext.collection._range).toHaveBeenCalledWith({ + expect(testContext.collection.range).toHaveBeenCalledWith({ params, limit: Infinity, callback, @@ -718,7 +718,7 @@ describe('RestfulModelCollection', () => { }); }); - describe('_range', () => { + describe('range', () => { beforeEach(() => { const threadsResponses = []; for (let x = 0; x <= 3; x++) { @@ -738,15 +738,15 @@ describe('RestfulModelCollection', () => { threadsResponses.push(response); } - testContext.collection._getModelCollection = jest.fn((params, offset) => + testContext.collection.getModelCollection = jest.fn((params, offset) => Promise.resolve(threadsResponses[offset / 100]) ); }); test('should fetch once if fewer than one page of models are requested', () => { const params = { from: 'ben@nylas.com' }; - testContext.collection._range({ params, offset: 0, limit: 50 }); - expect(testContext.collection._getModelCollection).toHaveBeenCalledWith( + testContext.collection.range({ params, offset: 0, limit: 50 }); + expect(testContext.collection.getModelCollection).toHaveBeenCalledWith( params, 0, 50, @@ -758,16 +758,16 @@ describe('RestfulModelCollection', () => { const params = { from: 'ben@nylas.com' }; const callback = () => { expect( - testContext.collection._getModelCollection.mock.calls[0] + testContext.collection.getModelCollection.mock.calls[0] ).toEqual([{ from: 'ben@nylas.com' }, 0, 100, '/threads']); expect( - testContext.collection._getModelCollection.mock.calls[1] + testContext.collection.getModelCollection.mock.calls[1] ).toEqual([{ from: 'ben@nylas.com' }, 100, 100, '/threads']); expect( - testContext.collection._getModelCollection.mock.calls[2] + testContext.collection.getModelCollection.mock.calls[2] ).toEqual([{ from: 'ben@nylas.com' }, 200, 100, '/threads']); }; - testContext.collection._range({ + testContext.collection.range({ params, callback, offset: 0, @@ -779,19 +779,19 @@ describe('RestfulModelCollection', () => { const params = { from: 'ben@nylas.com' }; const callback = () => { expect( - testContext.collection._getModelCollection.mock.calls[0] + testContext.collection.getModelCollection.mock.calls[0] ).toEqual([{ from: 'ben@nylas.com' }, 0, 100, '/threads']); expect( - testContext.collection._getModelCollection.mock.calls[1] + testContext.collection.getModelCollection.mock.calls[1] ).toEqual([{ from: 'ben@nylas.com' }, 100, 100, '/threads']); expect( - testContext.collection._getModelCollection.mock.calls[2] + testContext.collection.getModelCollection.mock.calls[2] ).toEqual([{ from: 'ben@nylas.com' }, 200, 100, '/threads']); expect( - testContext.collection._getModelCollection.mock.calls[3] + testContext.collection.getModelCollection.mock.calls[3] ).toEqual([{ from: 'ben@nylas.com' }, 300, 100, '/threads']); }; - testContext.collection._range({ + testContext.collection.range({ params, callback, offset: 0, @@ -801,7 +801,7 @@ describe('RestfulModelCollection', () => { test('should call the callback with all of the loaded models', done => { const params = { from: 'ben@nylas.com' }; - testContext.collection._range({ + testContext.collection.range({ params, offset: 0, limit: 10000, @@ -815,7 +815,7 @@ describe('RestfulModelCollection', () => { test('should resolve with the loaded models', done => { const params = { from: 'ben@nylas.com' }; testContext.collection - ._range({ params, offset: 0, limit: 10000 }) + .range({ params, offset: 0, limit: 10000 }) .then(function(models) { expect(models.length).toBe(313); done(); diff --git a/src/models/calendar.ts b/src/models/calendar.ts index 6679612d..7f9c5921 100644 --- a/src/models/calendar.ts +++ b/src/models/calendar.ts @@ -11,8 +11,8 @@ export default class Calendar extends RestfulModel { isPrimary?: boolean; jobStatusId?: string; - save(params: {} | SaveCallback = {}, callback?: SaveCallback) { - return this._save(params, callback); + protected save(params: {} | SaveCallback = {}, callback?: SaveCallback) { + return super.save(params, callback); } saveRequestBody() { diff --git a/src/models/contact.ts b/src/models/contact.ts index 1c28dc6f..7fddedcf 100644 --- a/src/models/contact.ts +++ b/src/models/contact.ts @@ -185,15 +185,15 @@ export class Contact extends RestfulModel { groups?: Group[]; source?: string; - save(params: {} | SaveCallback = {}, callback?: SaveCallback) { - return this._save(params, callback); + protected save(params: {} | SaveCallback = {}, callback?: SaveCallback) { + return super.save(params, callback); } getPicture( params: { [key: string]: any } = {}, callback?: (error: Error | null, result?: any) => void ) { - return this._get(params, callback, '/picture'); + return this.get(params, callback, '/picture'); } } diff --git a/src/models/delta-stream.ts b/src/models/delta-stream.ts index fc6b85f2..7c109ee2 100644 --- a/src/models/delta-stream.ts +++ b/src/models/delta-stream.ts @@ -57,7 +57,7 @@ export default class DeltaStream extends EventEmitter { } this.restartBackoff.failAfter(DeltaStream.MAX_RESTART_RETRIES); this.restartBackoff - .on('backoff', this._restartConnection.bind(this)) + .on('backoff', this.restartConnection.bind(this)) .on('fail', () => { return this.emit( 'error', @@ -115,16 +115,16 @@ export default class DeltaStream extends EventEmitter { // Do nothing } // Do nothing, keep err as string. - return this._onError(err); + return this.onError(err); }); return; } // Successfully established connection this.emit('response', response); - this._onDataReceived(); + this.onDataReceived(); return ( response.body - .on('data', this._onDataReceived.bind(this)) + .on('data', this.onDataReceived.bind(this)) // Each data block received may not be a complete JSON object. Pipe through // JSONStream.parse(), which handles converting data blocks to JSON objects. .pipe(JSONStream.parse()) @@ -136,11 +136,11 @@ export default class DeltaStream extends EventEmitter { }) ); } catch (error) { - this._onError(error); + this.onError(error); } } - _onDataReceived() { + private onDataReceived() { // Nylas sends a newline heartbeat in the raw data stream once every 5 seconds. // Automatically restart the connection if we haven't gotten any data in // Delta.streamingTimeoutMs. The connection will restart with the last @@ -153,12 +153,12 @@ export default class DeltaStream extends EventEmitter { ) as any; } - _onError(err: Error) { + private onError(err: Error) { this.emit('error', err); return this.restartBackoff.reset(); } - _restartConnection(n: number) { + private restartConnection(n: number) { this.emit( 'info', `Restarting Nylas DeltaStream connection (attempt ${n + 1}): ${ diff --git a/src/models/draft.ts b/src/models/draft.ts index 2587483c..92b49077 100644 --- a/src/models/draft.ts +++ b/src/models/draft.ts @@ -22,7 +22,7 @@ export default class Draft extends Message { return json; } - save(params: {} | SaveCallback = {}, callback?: SaveCallback) { + protected save(params: {} | SaveCallback = {}, callback?: SaveCallback) { if (this.rawMime) { const err = new Error('save() cannot be called for raw MIME drafts'); if (callback) { @@ -30,7 +30,7 @@ export default class Draft extends Message { } return Promise.reject(err); } - return this._save(params, callback); + return super.save(params, callback); } saveRequestBody() { diff --git a/src/models/event.ts b/src/models/event.ts index 48eb3de7..7254faf5 100644 --- a/src/models/event.ts +++ b/src/models/event.ts @@ -112,7 +112,7 @@ export default class Event extends RestfulModel { return qs; } - save(params: {} | SaveCallback = {}, callback?: SaveCallback) { + protected save(params: {} | SaveCallback = {}, callback?: SaveCallback) { if ( this.conferencing && this.conferencing.details && @@ -124,7 +124,7 @@ export default class Event extends RestfulModel { ) ); } - return this._save(params, callback); + return super.save(params, callback); } rsvp( diff --git a/src/models/folder.ts b/src/models/folder.ts index a1838f42..e97741ee 100644 --- a/src/models/folder.ts +++ b/src/models/folder.ts @@ -12,8 +12,8 @@ export class Folder extends RestfulModel { return json; } - save(params: {} | SaveCallback = {}, callback?: SaveCallback) { - return this._save(params, callback); + protected save(params: {} | SaveCallback = {}, callback?: SaveCallback) { + return super.save(params, callback); } } Folder.collectionName = 'folders'; diff --git a/src/models/management-model-collection.ts b/src/models/management-model-collection.ts index e03db17f..3c7de7a7 100644 --- a/src/models/management-model-collection.ts +++ b/src/models/management-model-collection.ts @@ -18,7 +18,7 @@ export default class ManagementModelCollection< this.path = `/a/${this.clientId}/${this.modelClass.collectionName}`; } - _createModel(json: { [key: string]: any }) { + protected createModel(json: { [key: string]: any }) { return new (this.modelClass as any)(this.connection, this.clientId, json); } } diff --git a/src/models/message.ts b/src/models/message.ts index 9b060ca5..cacbc5c7 100644 --- a/src/models/message.ts +++ b/src/models/message.ts @@ -83,8 +83,8 @@ export default class Message extends RestfulModel { return json; } - save(params: {} | SaveCallback = {}, callback?: SaveCallback) { - return this._save(params, callback); + protected save(params: {} | SaveCallback = {}, callback?: SaveCallback) { + return super.save(params, callback); } } Message.collectionName = 'messages'; diff --git a/src/models/model-collection.ts b/src/models/model-collection.ts index 3631ed62..092a1386 100644 --- a/src/models/model-collection.ts +++ b/src/models/model-collection.ts @@ -42,7 +42,7 @@ export default class ModelCollection { let offset = 0; const iteratee = (): Promise => { - return this._getItems(params, offset, REQUEST_CHUNK_SIZE).then(items => { + return this.getItems(params, offset, REQUEST_CHUNK_SIZE).then(items => { for (const item of items) { eachCallback(item); } @@ -82,7 +82,7 @@ export default class ModelCollection { const limit = params.limit || Infinity; const offset = params.offset; - return this._range({ params, offset, limit, callback }); + return this.range({ params, offset, limit, callback }); } find( @@ -123,7 +123,7 @@ export default class ModelCollection { return Promise.reject(err); } - return this._getModel(id, params) + return this.getModel(id, params) .then(model => { if (callback) { callback(null, model); @@ -138,7 +138,7 @@ export default class ModelCollection { }); } - _range({ + protected range({ params = {}, offset = 0, limit = 100, @@ -159,7 +159,7 @@ export default class ModelCollection { REQUEST_CHUNK_SIZE, limit - accumulated.length ); - return this._getItems(params, chunkOffset, chunkLimit, path).then( + return this.getItems(params, chunkOffset, chunkLimit, path).then( items => { accumulated = accumulated.concat(items); const finished = @@ -189,7 +189,7 @@ export default class ModelCollection { ); } - _getItems( + protected getItems( params: { [key: string]: any }, offset: number, limit: number, @@ -209,14 +209,14 @@ export default class ModelCollection { }); } - return this._getModelCollection(params, offset, limit, path); + return this.getModelCollection(params, offset, limit, path); } - _createModel(json: { [key: string]: any }): T { + protected createModel(json: { [key: string]: any }): T { return new this.modelClass(json) as T; } - _getModel(id: string, params: { [key: string]: any } = {}): Promise { + private getModel(id: string, params: { [key: string]: any } = {}): Promise { return this.connection .request({ method: 'GET', @@ -224,12 +224,12 @@ export default class ModelCollection { qs: params, }) .then((json: any) => { - const model = this._createModel(json); + const model = this.createModel(json); return Promise.resolve(model); }); } - _getModelCollection( + private getModelCollection( params: { [key: string]: any }, offset: number, limit: number, @@ -243,7 +243,7 @@ export default class ModelCollection { }) .then((jsonArray: any) => { const models = jsonArray.map((json: any) => { - return this._createModel(json); + return this.createModel(json); }); return Promise.resolve(models); }); diff --git a/src/models/neural.ts b/src/models/neural.ts index 5fa12f80..5dc2509f 100644 --- a/src/models/neural.ts +++ b/src/models/neural.ts @@ -20,7 +20,7 @@ export default class Neural extends RestfulModel { const body = { message_id: messageIds }; const path = 'sentiment'; - return this._request(path, body).then((jsonArray: any) => { + return this.request(path, body).then((jsonArray: any) => { return jsonArray.map((json: any) => { return new NeuralSentimentAnalysis(this.connection, json); }); @@ -31,7 +31,7 @@ export default class Neural extends RestfulModel { const body = { text: text }; const path = 'sentiment'; - return this._request(path, body).then(json => { + return this.request(path, body).then(json => { return new NeuralSentimentAnalysis(this.connection, json); }); } @@ -54,7 +54,7 @@ export default class Neural extends RestfulModel { body['parse_contact'] = parseContact; } - return this._request(path, body).then((jsonArray: any) => { + return this.request(path, body).then((jsonArray: any) => { return jsonArray.map((json: any) => { return new NeuralSignatureExtraction(this.connection, json); }); @@ -69,7 +69,7 @@ export default class Neural extends RestfulModel { body['pages'] = pages; } - return this._request(path, body).then(json => { + return this.request(path, body).then(json => { return new NeuralOcr(this.connection, json); }); } @@ -78,7 +78,7 @@ export default class Neural extends RestfulModel { const body = { message_id: messageIds }; const path = 'categorize'; - return this._request(path, body).then((jsonArray: any) => { + return this.request(path, body).then((jsonArray: any) => { return jsonArray.map((json: any) => { return new NeuralCategorizer(this.connection, json); }); @@ -99,14 +99,14 @@ export default class Neural extends RestfulModel { }; } - return this._request(path, body).then((jsonArray: any) => { + return this.request(path, body).then((jsonArray: any) => { return jsonArray.map((json: any) => { return new NeuralCleanConversation(this.connection, json); }); }); } - _request(path?: string, body?: object): Promise { + private request(path?: string, body?: object): Promise { if (!path) { path = (this.constructor as any).collectionName; } diff --git a/src/models/restful-model-collection.ts b/src/models/restful-model-collection.ts index c217a822..dbda6a4c 100644 --- a/src/models/restful-model-collection.ts +++ b/src/models/restful-model-collection.ts @@ -53,7 +53,7 @@ export default class RestfulModelCollection< return Promise.reject(err); } - return this._getItems(params, 0, 1) + return this.getItems(params, 0, 1) .then(items => { if (callback) { callback(null, items[0]); @@ -96,7 +96,7 @@ export default class RestfulModelCollection< const offset = params.offset; const path = `${this.path}/search`; - return this._range({ params, offset, limit, path }); + return this.range({ params, offset, limit, path }); } delete( @@ -162,7 +162,7 @@ export default class RestfulModelCollection< } build(args: { [key: string]: any }): T { - const model = this._createModel({}); + const model = this.createModel({}); for (const key in args) { const val = args[key]; (model as any)[key] = val; @@ -170,7 +170,7 @@ export default class RestfulModelCollection< return model; } - _createModel(json: { [key: string]: any }): T { + protected createModel(json: { [key: string]: any }): T { return new this.modelClass(this.connection, json) as T; } } diff --git a/src/models/restful-model.ts b/src/models/restful-model.ts index 18c32ec7..47835605 100644 --- a/src/models/restful-model.ts +++ b/src/models/restful-model.ts @@ -84,8 +84,8 @@ export default class RestfulModel extends Model { // Not every model needs to have a save function, but those who // do shouldn't have to reimplement the same boilerplate. - // They should instead define a save() function which calls _save. - _save(params: {} | SaveCallback = {}, callback?: SaveCallback): Promise { + // They should instead define a save() function which calls save. + protected save(params: {} | SaveCallback = {}, callback?: SaveCallback): Promise { if (typeof params === 'function') { callback = params as SaveCallback; params = {}; @@ -114,7 +114,7 @@ export default class RestfulModel extends Model { }); } - _get( + protected get( params: { [key: string]: any } = {}, callback?: (error: Error | null, result?: any) => void, pathSuffix = '' diff --git a/src/models/thread.ts b/src/models/thread.ts index 07071c19..6e6ca766 100644 --- a/src/models/thread.ts +++ b/src/models/thread.ts @@ -41,8 +41,8 @@ export default class Thread extends RestfulModel { return json; } - save(params: {} | SaveCallback = {}, callback?: SaveCallback) { - return this._save(params, callback); + protected save(params: {} | SaveCallback = {}, callback?: SaveCallback) { + return super.save(params, callback); } } Thread.collectionName = 'threads'; diff --git a/src/models/webhook.ts b/src/models/webhook.ts index 8088917e..324b6b21 100644 --- a/src/models/webhook.ts +++ b/src/models/webhook.ts @@ -25,8 +25,8 @@ export default class Webhook extends ManagementModel { } return json; } - save(params: {} | SaveCallback = {}, callback?: SaveCallback) { - return this._save(params, callback); + protected save(params: {} | SaveCallback = {}, callback?: SaveCallback) { + return super.save(params, callback); } } diff --git a/src/nylas-connection.ts b/src/nylas-connection.ts index 31d20a81..33dca464 100644 --- a/src/nylas-connection.ts +++ b/src/nylas-connection.ts @@ -174,7 +174,7 @@ export default class NylasConnection { }); } - _getWarningForVersion(sdkApiVersion?: string, apiVersion?: string) { + private getWarningForVersion(sdkApiVersion?: string, apiVersion?: string) { let warning = ''; if (sdkApiVersion != apiVersion) { @@ -210,7 +210,7 @@ export default class NylasConnection { | string | undefined; - const warning = this._getWarningForVersion( + const warning = this.getWarningForVersion( SUPPORTED_API_VERSION, apiVersion );