mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-02-02 13:27:55 +08:00
Fold EdgehillAPI, LegacyEdgehillAPI, NylasAPI together
This commit is contained in:
parent
20490a33b0
commit
5a8dab4c71
26 changed files with 388 additions and 483 deletions
|
@ -2,7 +2,7 @@ import {shell} from 'electron'
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import {RetinaImg} from 'nylas-component-kit';
|
||||
import {NylasAPI, Actions} from 'nylas-exports';
|
||||
import {NylasAPIRequest, Actions} from 'nylas-exports';
|
||||
|
||||
import OnboardingActions from '../onboarding-actions';
|
||||
import {runAuthValidation} from '../onboarding-helpers';
|
||||
|
@ -135,7 +135,7 @@ const CreatePageForForm = (FormComponent) => {
|
|||
errorFieldNames.push('email');
|
||||
errorFieldNames.push('username');
|
||||
}
|
||||
if (NylasAPI.TimeoutErrorCodes.includes(err.statusCode)) { // timeout
|
||||
if (NylasAPIRequest.TimeoutErrorCodes.includes(err.statusCode)) { // timeout
|
||||
errorMessage = "We were unable to reach your mail provider. Please try again."
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ const OnboardingActions = Reflux.createActions([
|
|||
"setAccountType",
|
||||
"moveToPreviousPage",
|
||||
"moveToPage",
|
||||
"authenticationJSONReceived",
|
||||
"identityJSONReceived",
|
||||
"accountJSONReceived",
|
||||
]);
|
||||
|
||||
|
|
|
@ -3,14 +3,12 @@
|
|||
import crypto from 'crypto';
|
||||
import {CommonProviderSettings} from 'imap-provider-settings';
|
||||
import {
|
||||
N1CloudAPI,
|
||||
NylasAPI,
|
||||
NylasAPIRequest,
|
||||
RegExpUtils,
|
||||
Utils,
|
||||
MailsyncProcess,
|
||||
} from 'nylas-exports';
|
||||
|
||||
const {makeRequest, rootURLForServer} = NylasAPIRequest;
|
||||
|
||||
const IMAP_FIELDS = new Set([
|
||||
"imap_host",
|
||||
|
@ -41,40 +39,32 @@ function base64url(inBuffer) {
|
|||
.replace(/\//g, '_'); // Convert '/' to '_'
|
||||
}
|
||||
|
||||
const NO_AUTH = { user: '', pass: '', sendImmediately: true };
|
||||
|
||||
export async function makeGmailOAuthRequest(sessionKey) {
|
||||
const remoteRequest = new NylasAPIRequest({
|
||||
api: N1CloudAPI,
|
||||
options: {
|
||||
path: `/auth/gmail/token?key=${sessionKey}`,
|
||||
method: 'GET',
|
||||
auth: NO_AUTH,
|
||||
},
|
||||
export function makeGmailOAuthRequest(sessionKey) {
|
||||
return makeRequest({
|
||||
server: 'accounts',
|
||||
path: `/auth/gmail/token?key=${sessionKey}`,
|
||||
method: 'GET',
|
||||
auth: false,
|
||||
});
|
||||
return remoteRequest.run()
|
||||
}
|
||||
|
||||
export async function authIMAPForGmail(tokenData) {
|
||||
const localRequest = new NylasAPIRequest({
|
||||
api: NylasAPI,
|
||||
options: {
|
||||
path: `/auth`,
|
||||
method: 'POST',
|
||||
auth: NO_AUTH,
|
||||
timeout: 1000 * 90, // Connecting to IMAP could take up to 90 seconds, so we don't want to hang up too soon
|
||||
body: {
|
||||
email: tokenData.email_address,
|
||||
name: tokenData.name,
|
||||
provider: 'gmail',
|
||||
settings: {
|
||||
xoauth2: tokenData.resolved_settings.xoauth2,
|
||||
expiry_date: tokenData.resolved_settings.expiry_date,
|
||||
},
|
||||
const localJSON = await makeRequest({
|
||||
server: 'accounts',
|
||||
path: `/auth`,
|
||||
method: 'POST',
|
||||
auth: false,
|
||||
timeout: 1000 * 90, // Connecting to IMAP could take up to 90 seconds, so we don't want to hang up too soon
|
||||
body: {
|
||||
email: tokenData.email_address,
|
||||
name: tokenData.name,
|
||||
provider: 'gmail',
|
||||
settings: {
|
||||
xoauth2: tokenData.resolved_settings.xoauth2,
|
||||
expiry_date: tokenData.resolved_settings.expiry_date,
|
||||
},
|
||||
},
|
||||
})
|
||||
const localJSON = await localRequest.run()
|
||||
const account = Object.assign({}, localJSON);
|
||||
account.localToken = localJSON.account_token;
|
||||
account.cloudToken = tokenData.account_token;
|
||||
|
@ -86,14 +76,14 @@ export function buildGmailSessionKey() {
|
|||
}
|
||||
|
||||
export function buildGmailAuthURL(sessionKey) {
|
||||
return `${N1CloudAPI.APIRoot}/auth/gmail?state=${sessionKey}`;
|
||||
return `${rootURLForServer('accounts')}/auth/gmail?state=${sessionKey}`;
|
||||
}
|
||||
|
||||
export function runAuthValidation(accountInfo) {
|
||||
export async function runAuthValidation(accountInfo) {
|
||||
const {username, type, email, name} = accountInfo;
|
||||
|
||||
const data = {
|
||||
id: Utils.generateTempId(), // TODO BG: Server will decide account ids
|
||||
id: 'temp',
|
||||
provider: type,
|
||||
name: name,
|
||||
emailAddress: email,
|
||||
|
@ -123,32 +113,23 @@ export function runAuthValidation(accountInfo) {
|
|||
// If this succeeds, send the received code to N1 server to register the account
|
||||
// Otherwise process the error message from the server and highlight UI as needed
|
||||
const proc = new MailsyncProcess(NylasEnv.getLoadSettings(), data);
|
||||
return proc.test().then((accountJSON) => {
|
||||
return accountJSON;
|
||||
});
|
||||
const {account} = await proc.test();
|
||||
|
||||
// TODO BG Re-enable cloud services
|
||||
// return n1CloudIMAPAuthRequest.run().then((remoteJSON) => {
|
||||
// const localSyncIMAPAuthRequest = new NylasAPIRequest({
|
||||
// api: NylasAPI,
|
||||
// options: {
|
||||
// path: `/auth`,
|
||||
// method: 'POST',
|
||||
// timeout: 1000 * 180, // Same timeout as server timeout (most requests are faster than 90s, but server validation can be slow in some cases)
|
||||
// body: data,
|
||||
// auth: {
|
||||
// user: '',
|
||||
// pass: '',
|
||||
// sendImmediately: true,
|
||||
// },
|
||||
// },
|
||||
// })
|
||||
// return localSyncIMAPAuthRequest.run().then((localJSON) => {
|
||||
// const accountWithTokens = Object.assign({}, localJSON);
|
||||
// accountWithTokens.localToken = localJSON.account_token;
|
||||
// accountWithTokens.cloudToken = '';//remoteJSON.account_token;
|
||||
// return accountWithTokens
|
||||
// })
|
||||
delete data.id;
|
||||
|
||||
const {id, account_token} = await makeRequest({
|
||||
server: 'accounts',
|
||||
path: `/auth`,
|
||||
method: 'POST',
|
||||
timeout: 1000 * 180, // Same timeout as server timeout (most requests are faster than 90s, but server validation can be slow in some cases)
|
||||
body: data,
|
||||
auth: false,
|
||||
})
|
||||
|
||||
return {
|
||||
account: Object.assign({}, account, {id}),
|
||||
cloudToken: account_token,
|
||||
};
|
||||
}
|
||||
|
||||
export function isValidHost(value) {
|
||||
|
|
|
@ -18,13 +18,10 @@ class OnboardingStore extends NylasStore {
|
|||
constructor() {
|
||||
super();
|
||||
|
||||
NylasEnv.config.onDidChange('env', this._onEnvChanged);
|
||||
this._onEnvChanged();
|
||||
|
||||
this.listenTo(OnboardingActions.moveToPreviousPage, this._onMoveToPreviousPage)
|
||||
this.listenTo(OnboardingActions.moveToPage, this._onMoveToPage)
|
||||
this.listenTo(OnboardingActions.accountJSONReceived, this._onAccountJSONReceived)
|
||||
this.listenTo(OnboardingActions.authenticationJSONReceived, this._onAuthenticationJSONReceived)
|
||||
this.listenTo(OnboardingActions.identityJSONReceived, this._onIdentityJSONReceived)
|
||||
this.listenTo(OnboardingActions.setAccountInfo, this._onSetAccountInfo);
|
||||
this.listenTo(OnboardingActions.setAccountType, this._onSetAccountType);
|
||||
ipcRenderer.on('set-account-type', (e, type) => {
|
||||
|
@ -80,19 +77,6 @@ class OnboardingStore extends NylasStore {
|
|||
}
|
||||
}
|
||||
|
||||
_onEnvChanged = () => {
|
||||
const env = NylasEnv.config.get('env')
|
||||
if (['development', 'local'].includes(env)) {
|
||||
this.welcomeRoot = "http://0.0.0.0:5555";
|
||||
} else if (env === 'experimental') {
|
||||
this.welcomeRoot = "https://www-experimental.nylas.com";
|
||||
} else if (env === 'staging') {
|
||||
this.welcomeRoot = "https://www-staging.nylas.com";
|
||||
} else {
|
||||
this.welcomeRoot = "https://nylas.com";
|
||||
}
|
||||
}
|
||||
|
||||
_onOnboardingComplete = () => {
|
||||
// When account JSON is received, we want to notify external services
|
||||
// that it succeeded. Unfortunately in this case we're likely to
|
||||
|
@ -137,7 +121,7 @@ class OnboardingStore extends NylasStore {
|
|||
this.trigger();
|
||||
}
|
||||
|
||||
_onAuthenticationJSONReceived = async (json) => {
|
||||
_onIdentityJSONReceived = async (json) => {
|
||||
const isFirstAccount = AccountStore.accounts().length === 0;
|
||||
|
||||
await IdentityStore.saveIdentity(json);
|
||||
|
@ -145,8 +129,8 @@ class OnboardingStore extends NylasStore {
|
|||
setTimeout(() => {
|
||||
if (isFirstAccount) {
|
||||
this._onSetAccountInfo(Object.assign({}, this._accountInfo, {
|
||||
name: `${json.firstname || ""} ${json.lastname || ""}`,
|
||||
email: json.email,
|
||||
name: `${json.firstName || ""} ${json.lastName || ""}`,
|
||||
email: json.emailAddress,
|
||||
}));
|
||||
OnboardingActions.moveToPage('account-choose');
|
||||
} else {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import {IdentityStore} from 'nylas-exports';
|
||||
import {NylasAPIRequest} from 'nylas-exports';
|
||||
import {Webview} from 'nylas-component-kit';
|
||||
import OnboardingActions from './onboarding-actions';
|
||||
|
||||
|
@ -12,18 +12,18 @@ export default class AuthenticatePage extends React.Component {
|
|||
|
||||
_src() {
|
||||
const n1Version = NylasEnv.getVersion();
|
||||
return `${IdentityStore.URLRoot}/onboarding?utm_medium=N1&utm_source=OnboardingPage&N1_version=${n1Version}&client_edition=basic`
|
||||
return `${NylasAPIRequest.rootURLForServer('identity')}/onboarding?utm_medium=N1&utm_source=OnboardingPage&N1_version=${n1Version}&client_edition=basic`
|
||||
}
|
||||
|
||||
_onDidFinishLoad = (webview) => {
|
||||
const receiveUserInfo = `
|
||||
var a = document.querySelector('#pro-account');
|
||||
var a = document.querySelector('#identity-result');
|
||||
result = a ? a.innerText : null;
|
||||
`;
|
||||
webview.executeJavaScript(receiveUserInfo, false, (result) => {
|
||||
this.setState({ready: true, webviewLoading: false});
|
||||
if (result !== null) {
|
||||
OnboardingActions.authenticationJSONReceived(JSON.parse(result));
|
||||
OnboardingActions.identityJSONReceived(JSON.parse(atob(result)));
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ path = require 'path'
|
|||
fs = require 'fs'
|
||||
_ = require 'underscore'
|
||||
{RetinaImg, Flexbox, ConfigPropContainer, NewsletterSignup} = require 'nylas-component-kit'
|
||||
{EdgehillAPI, AccountStore} = require 'nylas-exports'
|
||||
{AccountStore} = require 'nylas-exports'
|
||||
OnboardingActions = require('./onboarding-actions').default
|
||||
|
||||
# NOTE: Temporarily copied from preferences module
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# This file is in coffeescript just to use the existential operator!
|
||||
{AccountStore, LegacyEdgehillAPI} = require 'nylas-exports'
|
||||
{AccountStore} = require 'nylas-exports'
|
||||
|
||||
MAX_RETRY = 10
|
||||
|
||||
|
@ -12,7 +12,7 @@ module.exports = class ClearbitDataSource
|
|||
return Promise.resolve(null)
|
||||
new Promise (resolve, reject) =>
|
||||
return; # TODO BG
|
||||
req = LegacyEdgehillAPI.makeRequest({
|
||||
req = LegacyEdXgehillAPI.makeRequest({
|
||||
authWithNylasAPI: true
|
||||
path: "/proxy/clearbit/#{@clearbitAPI()}/find?email=#{email}",
|
||||
})
|
||||
|
|
|
@ -67,7 +67,7 @@ class PreferencesIdentity extends React.Component {
|
|||
|
||||
render() {
|
||||
const {identity} = this.state;
|
||||
const {firstname, lastname, email} = identity;
|
||||
const {firstName, lastName, emailAddress} = identity;
|
||||
|
||||
const logout = () => Actions.logoutNylasIdentity()
|
||||
|
||||
|
@ -83,8 +83,8 @@ class PreferencesIdentity extends React.Component {
|
|||
/>
|
||||
</div>
|
||||
<div className="identity-info">
|
||||
<div className="name">{firstname} {lastname}</div>
|
||||
<div className="email">{email}</div>
|
||||
<div className="name">{firstName} {lastName}</div>
|
||||
<div className="email">{emailAddress}</div>
|
||||
<div className="identity-actions">
|
||||
<OpenIdentityPageButton label="Account Details" path="/dashboard" source="Preferences" campaign="Dashboard" />
|
||||
<div className="btn minor-width" onClick={logout}>Sign Out</div>
|
||||
|
|
|
@ -35,9 +35,10 @@
|
|||
"enzyme": "2.9.1",
|
||||
"event-kit": "^1.0.2",
|
||||
"fs-plus": "^2.3.2",
|
||||
"getmac": "^1.2.1",
|
||||
"he": "1.1.0",
|
||||
"is-online": "7.0.0",
|
||||
"imap-provider-settings": "github:nylas/imap-provider-settings#2fdcd34d59b",
|
||||
"is-online": "7.0.0",
|
||||
"jasmine-json": "~0.0",
|
||||
"jasmine-react-helpers": "^0.2",
|
||||
"jasmine-reporters": "1.x.x",
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import {ipcRenderer} from 'electron';
|
||||
import {Utils, KeyManager, DatabaseWriter, SendFeatureUsageEventTask} from 'nylas-exports'
|
||||
import IdentityStore from '../../src/flux/stores/identity-store'
|
||||
import * as NylasAPIRequest from '../../src/flux/nylas-api-request'
|
||||
|
||||
const TEST_NYLAS_ID = "icihsnqh4pwujyqihlrj70vh"
|
||||
const TEST_TOKEN = "test-token"
|
||||
|
@ -67,13 +68,13 @@ describe("IdentityStore", function identityStoreSpec() {
|
|||
|
||||
it("can log a feature usage event", async () => {
|
||||
spyOn(IdentityStore, "saveIdentity").andReturn(Promise.resolve());
|
||||
spyOn(IdentityStore, "nylasIDRequest");
|
||||
spyOn(NylasAPIRequest, "makeRequest");
|
||||
IdentityStore._identity = this.identityJSON
|
||||
IdentityStore._identity.token = TEST_TOKEN;
|
||||
IdentityStore._onEnvChanged()
|
||||
const t = new SendFeatureUsageEventTask("snooze");
|
||||
await t.performRemote()
|
||||
const opts = IdentityStore.nylasIDRequest.calls[0].args[0]
|
||||
const opts = NylasAPIRequest.makeRequest.calls[0].args[0]
|
||||
expect(opts).toEqual({
|
||||
method: "POST",
|
||||
url: "https://billing.nylas.com/api/feature_usage_event",
|
||||
|
@ -104,7 +105,7 @@ describe("IdentityStore", function identityStoreSpec() {
|
|||
});
|
||||
});
|
||||
|
||||
describe("_fetchIdentity", () => {
|
||||
describe("fetchIdentity", () => {
|
||||
beforeEach(() => {
|
||||
IdentityStore._identity = this.identityJSON;
|
||||
spyOn(IdentityStore, "saveIdentity")
|
||||
|
@ -115,12 +116,12 @@ describe("IdentityStore", function identityStoreSpec() {
|
|||
it("saves the identity returned", async () => {
|
||||
const resp = Utils.deepClone(this.identityJSON);
|
||||
resp.feature_usage.feat.quota = 5
|
||||
spyOn(IdentityStore, "nylasIDRequest").andCallFake(() => {
|
||||
spyOn(NylasAPIRequest, "makeRequest").andCallFake(() => {
|
||||
return Promise.resolve(resp)
|
||||
})
|
||||
await IdentityStore._fetchIdentity();
|
||||
expect(IdentityStore.nylasIDRequest).toHaveBeenCalled();
|
||||
const options = IdentityStore.nylasIDRequest.calls[0].args[0]
|
||||
await IdentityStore.fetchIdentity();
|
||||
expect(NylasAPIRequest.makeRequest).toHaveBeenCalled();
|
||||
const options = NylasAPIRequest.makeRequest.calls[0].args[0]
|
||||
expect(options.url).toMatch(/\/n1\/user/)
|
||||
expect(IdentityStore.saveIdentity).toHaveBeenCalled()
|
||||
const newIdent = IdentityStore.saveIdentity.calls[0].args[0]
|
||||
|
@ -129,10 +130,10 @@ describe("IdentityStore", function identityStoreSpec() {
|
|||
});
|
||||
|
||||
it("errors if the json is invalid", async () => {
|
||||
spyOn(IdentityStore, "nylasIDRequest").andCallFake(() => {
|
||||
spyOn(NylasAPIRequest, "makeRequest").andCallFake(() => {
|
||||
return Promise.resolve({})
|
||||
})
|
||||
await IdentityStore._fetchIdentity();
|
||||
await IdentityStore.fetchIdentity();
|
||||
expect(NylasEnv.reportError).toHaveBeenCalled()
|
||||
expect(IdentityStore.saveIdentity).not.toHaveBeenCalled()
|
||||
});
|
||||
|
@ -140,10 +141,10 @@ describe("IdentityStore", function identityStoreSpec() {
|
|||
it("errors if the json doesn't match the ID", async () => {
|
||||
const resp = Utils.deepClone(this.identityJSON);
|
||||
resp.id = "THE WRONG ID"
|
||||
spyOn(IdentityStore, "nylasIDRequest").andCallFake(() => {
|
||||
spyOn(NylasAPIRequest, "makeRequest").andCallFake(() => {
|
||||
return Promise.resolve(resp)
|
||||
})
|
||||
await IdentityStore._fetchIdentity();
|
||||
await IdentityStore.fetchIdentity();
|
||||
expect(NylasEnv.reportError).toHaveBeenCalled()
|
||||
expect(IdentityStore.saveIdentity).not.toHaveBeenCalled()
|
||||
});
|
||||
|
|
|
@ -3,7 +3,6 @@ Folder = require('../../src/flux/models/folder').default
|
|||
Thread = require('../../src/flux/models/thread').default
|
||||
Message = require('../../src/flux/models/message').default
|
||||
Actions = require('../../src/flux/actions').default
|
||||
NylasAPI = require('../../src/flux/nylas-api').default
|
||||
Query = require('../../src/flux/models/query').default
|
||||
DatabaseStore = require('../../src/flux/stores/database-store').default
|
||||
ChangeFolderTask = require('../../src/flux/tasks/change-folder-task').default
|
||||
|
|
|
@ -3,7 +3,6 @@ Label = require('../../src/flux/models/label').default
|
|||
Thread = require('../../src/flux/models/thread').default
|
||||
Message = require('../../src/flux/models/message').default
|
||||
Actions = require('../../src/flux/actions').default
|
||||
NylasAPI = require('../../src/flux/nylas-api').default
|
||||
DatabaseStore = require('../../src/flux/stores/database-store').default
|
||||
ChangeLabelsTask = require('../../src/flux/tasks/change-labels-task').default
|
||||
ChangeMailTask = require('../../src/flux/tasks/change-mail-task').default
|
||||
|
|
|
@ -8,8 +8,6 @@ import {
|
|||
Contact,
|
||||
Task,
|
||||
SendDraftTask,
|
||||
NylasAPI,
|
||||
NylasAPIHelpers,
|
||||
NylasAPIRequest,
|
||||
SoundRegistry,
|
||||
SyncbackMetadataTask,
|
||||
|
|
|
@ -3,12 +3,15 @@ ReactDOM = require 'react-dom'
|
|||
{Utils,
|
||||
RegExpUtils,
|
||||
IdentityStore,
|
||||
NylasAPIRequest,
|
||||
SearchableComponentMaker,
|
||||
SearchableComponentStore}= require 'nylas-exports'
|
||||
SearchableComponentStore} = require 'nylas-exports'
|
||||
IFrameSearcher = require('../searchable-components/iframe-searcher').default
|
||||
url = require 'url'
|
||||
_ = require "underscore"
|
||||
|
||||
{rootURLForServer} = NylasAPIRequest
|
||||
|
||||
###
|
||||
Public: EventedIFrame is a thin wrapper around the DOM's standard `<iframe>` element.
|
||||
You should always use EventedIFrame, because it provides important event hooks that
|
||||
|
@ -168,8 +171,8 @@ class EventedIFrame extends React.Component
|
|||
|
||||
# If this is a link to our billing site, attempt single sign on instead of
|
||||
# just following the link directly
|
||||
if rawHref.startsWith(IdentityStore.URLRoot)
|
||||
path = rawHref.split(IdentityStore.URLRoot).pop()
|
||||
if rawHref.startsWith(rootURLForServer('identity'))
|
||||
path = rawHref.split(rootURLForServer('identity')).pop()
|
||||
IdentityStore.fetchSingleSignOnURL(path, {source: "SingleSignOnEmail"}).then (href) =>
|
||||
NylasEnv.windowEventHandler.openLink(href: href, metaKey: e.metaKey)
|
||||
return
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {React, Actions, NylasAPI, NylasAPIHelpers, APIError} from 'nylas-exports'
|
||||
import {React, Actions, NylasAPIRequest, NylasAPIHelpers, APIError} from 'nylas-exports'
|
||||
import {RetinaImg} from 'nylas-component-kit'
|
||||
import classnames from 'classnames'
|
||||
import _ from 'underscore'
|
||||
|
@ -74,7 +74,7 @@ export default class MetadataComposerToggleButton extends React.Component {
|
|||
NylasEnv.reportError(error);
|
||||
} else if (error.statusCode === 400) {
|
||||
NylasEnv.reportError(error);
|
||||
} else if (NylasAPI.TimeoutErrorCodes.includes(error.statusCode)) {
|
||||
} else if (NylasAPIRequest.TimeoutErrorCodes.includes(error.statusCode)) {
|
||||
title = "Offline"
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import _ from 'underscore';
|
||||
import React from 'react';
|
||||
import {LegacyEdgehillAPI} from "nylas-exports";
|
||||
import {RetinaImg, Flexbox} from 'nylas-component-kit';
|
||||
import {makeRequest} from '../flux/nylas-api-request';
|
||||
|
||||
export default class NewsletterSignup extends React.Component {
|
||||
static displayName = 'NewsletterSignup';
|
||||
|
@ -35,53 +35,54 @@ export default class NewsletterSignup extends React.Component {
|
|||
this.setState(state);
|
||||
}
|
||||
|
||||
_onGetStatus = (props = this.props) => {
|
||||
_onGetStatus = async (props = this.props) => {
|
||||
this._setState({status: 'Pending'});
|
||||
LegacyEdgehillAPI.makeRequest({
|
||||
method: 'GET',
|
||||
path: this._path(props),
|
||||
}).run().then((status) => {
|
||||
try {
|
||||
const status = await makeRequest({
|
||||
server: 'identity',
|
||||
method: 'GET',
|
||||
path: this._path(props),
|
||||
})
|
||||
if (status === 'Never Subscribed') {
|
||||
this._onSubscribe();
|
||||
} else {
|
||||
this._setState({status});
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
} catch (err) {
|
||||
this._setState({status: "Error"});
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
_onSubscribe = () => {
|
||||
_onSubscribe = async () => {
|
||||
this._setState({status: 'Pending'});
|
||||
LegacyEdgehillAPI.makeRequest({
|
||||
method: 'POST',
|
||||
path: this._path(),
|
||||
}).run()
|
||||
.then((status) => {
|
||||
try {
|
||||
const status = await makeRequest({
|
||||
server: 'identity',
|
||||
method: 'POST',
|
||||
path: this._path(),
|
||||
});
|
||||
this._setState({status});
|
||||
})
|
||||
.catch(() => {
|
||||
} catch (err) {
|
||||
this._setState({status: "Error"});
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
_onUnsubscribe = () => {
|
||||
this._setState({status: 'Pending'});
|
||||
LegacyEdgehillAPI.makeRequest({
|
||||
method: 'DELETE',
|
||||
path: this._path(),
|
||||
}).run()
|
||||
.then((status) => {
|
||||
try {
|
||||
const status = makeRequest({
|
||||
server: 'identity',
|
||||
method: 'DELETE',
|
||||
path: this._path(),
|
||||
});
|
||||
this._setState({status});
|
||||
})
|
||||
.catch(() => {
|
||||
} catch (err) {
|
||||
this._setState({status: "Error"});
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
_path(props = this.props) {
|
||||
return `/newsletter-subscription/${encodeURIComponent(props.emailAddress)}?name=${encodeURIComponent(props.name)}`;
|
||||
return `/api/newsletter-subscription/${encodeURIComponent(props.emailAddress)}?name=${encodeURIComponent(props.name)}`;
|
||||
}
|
||||
|
||||
_renderControl() {
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
import AccountStore from './stores/account-store'
|
||||
import IdentityStore from './stores/identity-store'
|
||||
import NylasAPIRequest from './nylas-api-request';
|
||||
|
||||
// We're currently moving between services hosted on edgehill-api (written in
|
||||
// Python) and services written in Node. Since we're doing this move progressively,
|
||||
// we need to be able to use the two services at once. That's why we have two
|
||||
// objects, EdgehillAPI (new API) and LegacyEdgehillAPI (old API).
|
||||
class _EdgehillAPI {
|
||||
constructor() {
|
||||
NylasEnv.config.onDidChange('env', this._onConfigChanged);
|
||||
this._onConfigChanged();
|
||||
}
|
||||
|
||||
_onConfigChanged = () => {
|
||||
const env = NylasEnv.config.get('env')
|
||||
if (['development', 'local'].includes(env)) {
|
||||
this.APIRoot = "http://n1-auth.lvh.me:5555";
|
||||
} else if (env === 'staging') {
|
||||
this.APIRoot = "https://n1-auth-staging.nylas.com";
|
||||
} else {
|
||||
this.APIRoot = "https://n1-auth.nylas.com";
|
||||
}
|
||||
}
|
||||
|
||||
accessTokenForAccountId(aid) {
|
||||
return AccountStore.tokensForAccountId(aid).n1Cloud
|
||||
}
|
||||
|
||||
makeRequest(options = {}) {
|
||||
if (NylasEnv.getLoadSettings().isSpec) {
|
||||
return {run: () => Promise.resolve()}
|
||||
}
|
||||
|
||||
if (options.authWithNylasAPI) {
|
||||
if (!IdentityStore.identity()) {
|
||||
throw new Error('LegacyEdgehillAPI.makeRequest: Identity must be present to make a request that auths with Nylas API')
|
||||
}
|
||||
// The account doesn't matter for Edgehill server. We just need to
|
||||
// ensure it's a valid account.
|
||||
options.accountId = AccountStore.accounts()[0].id;
|
||||
// The `NylasAPIRequest` object will grab the appropriate tokens.
|
||||
delete options.auth;
|
||||
delete options.authWithNylasAPI;
|
||||
} else {
|
||||
// A majority of Edgehill-server (aka auth) requests neither need
|
||||
// (nor have) account or N1 ID tokens to provide.
|
||||
// The existence of the options.auth object will prevent
|
||||
// `NylasAPIRequest` from constructing them from existing tokens
|
||||
options.auth = options.auth || {
|
||||
user: '',
|
||||
pass: '',
|
||||
sendImmediately: true,
|
||||
};
|
||||
}
|
||||
|
||||
const req = new NylasAPIRequest({
|
||||
api: this,
|
||||
options,
|
||||
});
|
||||
return req;
|
||||
}
|
||||
}
|
||||
|
||||
const EdgehillAPI = new _EdgehillAPI();
|
||||
export {EdgehillAPI, _EdgehillAPI};
|
|
@ -1,18 +0,0 @@
|
|||
// We use this class to access all the services we haven't migrated
|
||||
// yet to the new codebase.
|
||||
import {_EdgehillAPI} from './edgehill-api'
|
||||
|
||||
class LegacyEdgehillAPI extends _EdgehillAPI {
|
||||
_onConfigChanged = () => {
|
||||
const env = NylasEnv.config.get('env')
|
||||
if (['development', 'local'].includes(env)) {
|
||||
this.APIRoot = "http://localhost:5009";
|
||||
} else if (env === 'staging') {
|
||||
this.APIRoot = "https://edgehill-staging.nylas.com";
|
||||
} else {
|
||||
this.APIRoot = "https://edgehill.nylas.com";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new LegacyEdgehillAPI();
|
|
@ -1,161 +1,238 @@
|
|||
import Utils from './models/utils'
|
||||
import Actions from './actions'
|
||||
import {APIError} from './errors'
|
||||
import IdentityStore from './stores/identity-store'
|
||||
|
||||
// A 0 code is when an error returns without a status code, like "ESOCKETTIMEDOUT"
|
||||
export const TimeoutErrorCodes = [0, 408, "ETIMEDOUT", "ESOCKETTIMEDOUT", "ECONNRESET", "ENETDOWN", "ENETUNREACH"]
|
||||
export const PermanentErrorCodes = [400, 401, 402, 403, 404, 405, 429, 500, "ENOTFOUND", "ECONNREFUSED", "EHOSTDOWN", "EHOSTUNREACH"]
|
||||
export const CanceledErrorCodes = [-123, "ECONNABORTED"]
|
||||
export const SampleTemporaryErrorCode = 504
|
||||
|
||||
export default class NylasAPIRequest {
|
||||
// server option
|
||||
|
||||
constructor({api, options}) {
|
||||
const defaults = {
|
||||
url: `${options.APIRoot || api.APIRoot}${options.path}`,
|
||||
method: 'GET',
|
||||
json: true,
|
||||
timeout: 30000,
|
||||
started: () => {},
|
||||
}
|
||||
export function rootURLForServer(server) {
|
||||
const env = NylasEnv.config.get('env');
|
||||
|
||||
this.api = api;
|
||||
this.options = Object.assign(defaults, options);
|
||||
this.response = null
|
||||
|
||||
const bodyIsRequired = (this.options.method !== 'GET' && !this.options.formData);
|
||||
if (bodyIsRequired) {
|
||||
const fallback = this.options.json ? {} : '';
|
||||
this.options.body = this.options.body || fallback;
|
||||
}
|
||||
if (!['development', 'local', 'staging', 'production'].includes(env)) {
|
||||
throw new Error(`rootURLForServer: ${env} is not a valid environment.`);
|
||||
}
|
||||
|
||||
async run() {
|
||||
if (server === 'identity') {
|
||||
return {
|
||||
local: "http://localhost:5101",
|
||||
development: "http://localhost:5101",
|
||||
staging: "https://id-staging.nylas.com",
|
||||
production: "https://id.nylas.com",
|
||||
}[env];
|
||||
}
|
||||
if (server === 'accounts') {
|
||||
return {
|
||||
local: "http://localhost:5100",
|
||||
development: "http://localhost:5100",
|
||||
staging: "https://accounts-staging.nylas.com",
|
||||
production: "https://accounts.nylas.com",
|
||||
}[env];
|
||||
}
|
||||
|
||||
throw new Error("rootURLForServer: You must provide a valid `server` value");
|
||||
}
|
||||
|
||||
export async function makeRequest(options) {
|
||||
try {
|
||||
const root = rootURLForServer(options.server);
|
||||
|
||||
options.headers = options.headers || new Headers();
|
||||
options.headers.set('Accept', 'application/json');
|
||||
options.credentials = 'include';
|
||||
|
||||
if (!options.auth) {
|
||||
if (options.server === 'identity') {
|
||||
options.headers.set('Authorization', `Basic ${btoa(`${IdentityStore._identity.token}:`)}`)
|
||||
}
|
||||
}
|
||||
|
||||
if (options.path) {
|
||||
options.url = `${root}${options.path}`;
|
||||
}
|
||||
|
||||
if (options.body && !(options.body instanceof FormData)) {
|
||||
options.headers.set('Content-Type', 'application/json');
|
||||
options.body = JSON.stringify(options.body);
|
||||
console.log(options.body);
|
||||
}
|
||||
|
||||
const resp = await fetch(options.url, options);
|
||||
if (!resp.ok) {
|
||||
throw new APIError(resp.statusText);
|
||||
}
|
||||
return resp.json();
|
||||
} catch (err) {
|
||||
const error = new APIError(err) || new APIError(`${options.url} returned ${err.message}.`)
|
||||
NylasEnv.reportError(error);
|
||||
return null;
|
||||
|
||||
// TODO BG: Promise.reject();
|
||||
|
||||
// if (NylasEnv.getLoadSettings().isSpec) return Promise.resolve([]);
|
||||
// try {
|
||||
// this.options.auth = this.options.auth || this._defaultAuth();
|
||||
// return await this._asyncRequest(this.options)
|
||||
// } catch (error) {
|
||||
// let apiError = error
|
||||
// if (!(apiError instanceof APIError)) {
|
||||
// apiError = new APIError({error: apiError, statusCode: 500})
|
||||
// }
|
||||
// this._notifyOfAPIError(apiError)
|
||||
// throw apiError
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* An async wrapper around `request`. We reject on any non 2xx codes or
|
||||
* other errors.
|
||||
*
|
||||
* Resolves to the JSON body or rejects with an APIError object.
|
||||
*/
|
||||
async _asyncRequest(options = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Blob requests can potentially contain megabytes of binary data.
|
||||
// it doesn't make sense to send them through the action bridge.
|
||||
const req = request(options, (error, response, body) => {
|
||||
this.response = response;
|
||||
const statusCode = (response || {}).statusCode;
|
||||
|
||||
if (statusCode >= 200 && statusCode <= 299) {
|
||||
return resolve(body)
|
||||
}
|
||||
|
||||
const apiError = new APIError({
|
||||
body: body,
|
||||
error: error,
|
||||
response: response,
|
||||
statusCode: statusCode,
|
||||
requestOptions: options,
|
||||
});
|
||||
return reject(apiError)
|
||||
});
|
||||
req.on('abort', () => {
|
||||
// Use a status code of 0 because we don't want to report the error when
|
||||
// we manually abort the request
|
||||
const statusCode = 0
|
||||
const abortedError = new APIError({
|
||||
statusCode,
|
||||
body: 'Request aborted by client',
|
||||
});
|
||||
reject(abortedError);
|
||||
});
|
||||
|
||||
req.on('aborted', () => {
|
||||
const statusCode = "ECONNABORTED"
|
||||
const abortedError = new APIError({
|
||||
statusCode,
|
||||
body: 'Request aborted by server',
|
||||
});
|
||||
reject(abortedError);
|
||||
});
|
||||
options.started(req);
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
async _notifyOfAPIError(apiError) {
|
||||
const {statusCode} = apiError
|
||||
// TODO move this check into NylasEnv.reportError()?
|
||||
if (apiError.shouldReportError()) {
|
||||
const msg = apiError.message || `Unknown Error: ${apiError}`
|
||||
const fingerprint = ["{{ default }}", "api error", this.options.url, apiError.statusCode, msg];
|
||||
NylasEnv.reportError(apiError, {fingerprint,
|
||||
rateLimit: {
|
||||
ratePerHour: 30,
|
||||
key: `APIError:${this.options.url}:${statusCode}:${msg}`,
|
||||
},
|
||||
});
|
||||
apiError.reported = true
|
||||
}
|
||||
|
||||
if ([401, 403].includes(statusCode)) {
|
||||
Actions.apiAuthError(apiError, this.options, this.api.constructor.name)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the basic auth username from the account token and the
|
||||
* basic auth password from the NylasID token.
|
||||
*
|
||||
* This asserts if any of these pieces are missing and throws an
|
||||
* APIError object.
|
||||
*/
|
||||
_defaultAuth() {
|
||||
try {
|
||||
if (!this.options.accountId) {
|
||||
throw new Error("Cannot make Nylas request without specifying `auth` or an `accountId`.");
|
||||
}
|
||||
|
||||
const identity = IdentityStore.identity();
|
||||
|
||||
if (!identity || !identity.token) {
|
||||
// const clickedIndex = remote.dialog.showMessageBox({
|
||||
// type: 'error',
|
||||
// message: 'Your NylasID is invalid. Please log out then log back in.',
|
||||
// detail: `Actions like sending and receiving mail require this token. Please log back into your Nylas ID to restore it—your email accounts will not be removed in this process.`,
|
||||
// buttons: ['Log out'],
|
||||
// })
|
||||
// if (clickedIndex === 0) {
|
||||
// Actions.logoutNylasIdentity()
|
||||
// }
|
||||
throw new Error("No Identity")
|
||||
}
|
||||
|
||||
const accountToken = this.api.accessTokenForAccountId(this.options.accountId);
|
||||
if (!accountToken) {
|
||||
throw new Error(`Auth token missing for account`);
|
||||
}
|
||||
|
||||
return {
|
||||
user: accountToken,
|
||||
pass: identity.token,
|
||||
sendImmediately: true,
|
||||
};
|
||||
} catch (error) {
|
||||
throw new APIError({error, statusCode: 400});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
TimeoutErrorCodes,
|
||||
PermanentErrorCodes,
|
||||
CanceledErrorCodes,
|
||||
SampleTemporaryErrorCode,
|
||||
rootURLForServer,
|
||||
makeRequest,
|
||||
}
|
||||
|
||||
// export default class NylasAPIRequest {
|
||||
|
||||
// constructor({api, options}) {
|
||||
// const defaults = {
|
||||
// url: `${options.APIRoot || api.APIRoot}${options.path}`,
|
||||
// method: 'GET',
|
||||
// json: true,
|
||||
// timeout: 30000,
|
||||
// started: () => {},
|
||||
// }
|
||||
|
||||
// this.api = api;
|
||||
// this.options = Object.assign(defaults, options);
|
||||
// this.response = null
|
||||
|
||||
// const bodyIsRequired = (this.options.method !== 'GET' && !this.options.formData);
|
||||
// if (bodyIsRequired) {
|
||||
// const fallback = this.options.json ? {} : '';
|
||||
// this.options.body = this.options.body || fallback;
|
||||
// }
|
||||
// }
|
||||
|
||||
// async run() {
|
||||
// return null;
|
||||
|
||||
// // TODO BG: Promise.reject();
|
||||
|
||||
// // if (NylasEnv.getLoadSettings().isSpec) return Promise.resolve([]);
|
||||
// // try {
|
||||
// // this.options.auth = this.options.auth || this._defaultAuth();
|
||||
// // return await this._asyncRequest(this.options)
|
||||
// // } catch (error) {
|
||||
// // let apiError = error
|
||||
// // if (!(apiError instanceof APIError)) {
|
||||
// // apiError = new APIError({error: apiError, statusCode: 500})
|
||||
// // }
|
||||
// // this._notifyOfAPIError(apiError)
|
||||
// // throw apiError
|
||||
// // }
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * An async wrapper around `request`. We reject on any non 2xx codes or
|
||||
// * other errors.
|
||||
// *
|
||||
// * Resolves to the JSON body or rejects with an APIError object.
|
||||
// */
|
||||
// async _asyncRequest(options = {}) {
|
||||
// return new Promise((resolve, reject) => {
|
||||
// // Blob requests can potentially contain megabytes of binary data.
|
||||
// // it doesn't make sense to send them through the action bridge.
|
||||
// const req = request(options, (error, response, body) => {
|
||||
// this.response = response;
|
||||
// const statusCode = (response || {}).statusCode;
|
||||
|
||||
// if (statusCode >= 200 && statusCode <= 299) {
|
||||
// return resolve(body)
|
||||
// }
|
||||
|
||||
// const apiError = new APIError({
|
||||
// body: body,
|
||||
// error: error,
|
||||
// response: response,
|
||||
// statusCode: statusCode,
|
||||
// requestOptions: options,
|
||||
// });
|
||||
// return reject(apiError)
|
||||
// });
|
||||
// req.on('abort', () => {
|
||||
// // Use a status code of 0 because we don't want to report the error when
|
||||
// // we manually abort the request
|
||||
// const statusCode = 0
|
||||
// const abortedError = new APIError({
|
||||
// statusCode,
|
||||
// body: 'Request aborted by client',
|
||||
// });
|
||||
// reject(abortedError);
|
||||
// });
|
||||
|
||||
// req.on('aborted', () => {
|
||||
// const statusCode = "ECONNABORTED"
|
||||
// const abortedError = new APIError({
|
||||
// statusCode,
|
||||
// body: 'Request aborted by server',
|
||||
// });
|
||||
// reject(abortedError);
|
||||
// });
|
||||
// options.started(req);
|
||||
// })
|
||||
// }
|
||||
|
||||
|
||||
// async _notifyOfAPIError(apiError) {
|
||||
// const {statusCode} = apiError
|
||||
// // TODO move this check into NylasEnv.reportError()?
|
||||
// if (apiError.shouldReportError()) {
|
||||
// const msg = apiError.message || `Unknown Error: ${apiError}`
|
||||
// const fingerprint = ["{{ default }}", "api error", this.options.url, apiError.statusCode, msg];
|
||||
// NylasEnv.reportError(apiError, {fingerprint,
|
||||
// rateLimit: {
|
||||
// ratePerHour: 30,
|
||||
// key: `APIError:${this.options.url}:${statusCode}:${msg}`,
|
||||
// },
|
||||
// });
|
||||
// apiError.reported = true
|
||||
// }
|
||||
|
||||
// if ([401, 403].includes(statusCode)) {
|
||||
// Actions.apiAuthError(apiError, this.options, this.api.constructor.name)
|
||||
// }
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Generates the basic auth username from the account token and the
|
||||
// * basic auth password from the NylasID token.
|
||||
// *
|
||||
// * This asserts if any of these pieces are missing and throws an
|
||||
// * APIError object.
|
||||
// */
|
||||
// _defaultAuth() {
|
||||
// try {
|
||||
// if (!this.options.accountId) {
|
||||
// throw new Error("Cannot make Nylas request without specifying `auth` or an `accountId`.");
|
||||
// }
|
||||
|
||||
// const identity = IdentityStore.identity();
|
||||
|
||||
// if (!identity || !identity.token) {
|
||||
// // const clickedIndex = remote.dialog.showMessageBox({
|
||||
// // type: 'error',
|
||||
// // message: 'Your NylasID is invalid. Please log out then log back in.',
|
||||
// // detail: `Actions like sending and receiving mail require this token. Please log back into your Nylas ID to restore it—your email accounts will not be removed in this process.`,
|
||||
// // buttons: ['Log out'],
|
||||
// // })
|
||||
// // if (clickedIndex === 0) {
|
||||
// // Actions.logoutNylasIdentity()
|
||||
// // }
|
||||
// throw new Error("No Identity")
|
||||
// }
|
||||
|
||||
// const accountToken = this.api.accessTokenForAccountId(this.options.accountId);
|
||||
// if (!accountToken) {
|
||||
// throw new Error(`Auth token missing for account`);
|
||||
// }
|
||||
|
||||
// return {
|
||||
// user: accountToken,
|
||||
// pass: identity.token,
|
||||
// sendImmediately: true,
|
||||
// };
|
||||
// } catch (error) {
|
||||
// throw new APIError({error, statusCode: 400});
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
import AccountStore from './stores/account-store'
|
||||
|
||||
// A 0 code is when an error returns without a status code, like "ESOCKETTIMEDOUT"
|
||||
const TimeoutErrorCodes = [0, 408, "ETIMEDOUT", "ESOCKETTIMEDOUT", "ECONNRESET", "ENETDOWN", "ENETUNREACH"]
|
||||
const PermanentErrorCodes = [400, 401, 402, 403, 404, 405, 429, 500, "ENOTFOUND", "ECONNREFUSED", "EHOSTDOWN", "EHOSTUNREACH"]
|
||||
const CanceledErrorCodes = [-123, "ECONNABORTED"]
|
||||
const SampleTemporaryErrorCode = 504
|
||||
|
||||
|
||||
class NylasAPI {
|
||||
|
||||
constructor() {
|
||||
let port = 2578;
|
||||
if (NylasEnv.inDevMode()) port = 1337;
|
||||
this.APIRoot = `http://localhost:${port}`
|
||||
|
||||
this.TimeoutErrorCodes = TimeoutErrorCodes
|
||||
this.PermanentErrorCodes = PermanentErrorCodes
|
||||
this.CanceledErrorCodes = CanceledErrorCodes
|
||||
this.SampleTemporaryErrorCode = SampleTemporaryErrorCode
|
||||
}
|
||||
|
||||
accessTokenForAccountId(aid) {
|
||||
return AccountStore.tokensForAccountId(aid).localSync
|
||||
}
|
||||
}
|
||||
|
||||
export default new NylasAPI()
|
|
@ -5,6 +5,7 @@ import url from 'url'
|
|||
import Utils from '../models/utils';
|
||||
import Actions from '../actions';
|
||||
import KeyManager from '../../key-manager';
|
||||
import {makeRequest, rootURLForServer} from '../nylas-api-request';
|
||||
|
||||
// Note this key name is used when migrating to Nylas Pro accounts from old N1.
|
||||
const KEYCHAIN_NAME = 'Nylas Account';
|
||||
|
@ -25,9 +26,6 @@ class IdentityStore extends NylasStore {
|
|||
return
|
||||
}
|
||||
|
||||
NylasEnv.config.onDidChange('env', this._onEnvChanged);
|
||||
this._onEnvChanged();
|
||||
|
||||
NylasEnv.config.onDidChange('nylasid', this._onIdentityChanged);
|
||||
this._onIdentityChanged();
|
||||
|
||||
|
@ -106,17 +104,6 @@ class IdentityStore extends NylasStore {
|
|||
remote.app.quit()
|
||||
}
|
||||
|
||||
_onEnvChanged = () => {
|
||||
const env = NylasEnv.config.get('env')
|
||||
if (['development', 'local'].includes(env)) {
|
||||
this.URLRoot = "http://localhost:5101";
|
||||
} else if (env === 'staging') {
|
||||
this.URLRoot = "https://billing-staging.nylas.com";
|
||||
} else {
|
||||
this.URLRoot = "https://billing.nylas.com";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This passes utm_source, utm_campaign, and utm_content params to the
|
||||
* N1 billing site. Please reference:
|
||||
|
@ -151,16 +138,17 @@ class IdentityStore extends NylasStore {
|
|||
body.append('next_path', pathWithUtm);
|
||||
|
||||
try {
|
||||
const json = await this.nylasIDRequest({
|
||||
const json = await makeRequest({
|
||||
server: 'identity',
|
||||
path: '/api/login-link',
|
||||
qs: qs,
|
||||
body: body,
|
||||
timeout: 1500,
|
||||
method: 'POST',
|
||||
});
|
||||
return `${this.URLRoot}${json.path}`;
|
||||
return `${rootURLForServer('identity')}${json.path}`;
|
||||
} catch (err) {
|
||||
return `${this.URLRoot}${path}`;
|
||||
return `${rootURLForServer('identity')}${path}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -168,7 +156,13 @@ class IdentityStore extends NylasStore {
|
|||
if (!this._identity || !this._identity.token) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
const json = await this.nylasIDRequest({path: '/api/me', method: 'GET'});
|
||||
|
||||
const json = await makeRequest({
|
||||
server: 'identity',
|
||||
path: '/api/me',
|
||||
method: 'GET',
|
||||
});
|
||||
|
||||
if (!json || !json.id || json.id !== this._identity.id) {
|
||||
console.error(json)
|
||||
NylasEnv.reportError(new Error("Remote Identity returned invalid json"), json || {})
|
||||
|
@ -177,26 +171,6 @@ class IdentityStore extends NylasStore {
|
|||
const nextIdentity = Object.assign({}, this._identity, json);
|
||||
return this.saveIdentity(nextIdentity);
|
||||
}
|
||||
|
||||
async nylasIDRequest(options) {
|
||||
try {
|
||||
if (options.path) {
|
||||
options.url = `${this.URLRoot}${options.path}`;
|
||||
}
|
||||
options.credentials = 'include';
|
||||
options.headers = new Headers();
|
||||
options.headers.set('Authorization', `Basic ${btoa(`${this._identity.token}:`)}`)
|
||||
const resp = await fetch(options.url, options);
|
||||
if (!resp.ok) {
|
||||
throw new Error(resp.statusText);
|
||||
}
|
||||
return resp.json();
|
||||
} catch (err) {
|
||||
const error = err || new Error(`IdentityStore.nylasIDRequest: ${options.url} ${err.message}.`)
|
||||
NylasEnv.reportError(error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new IdentityStore()
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import NylasStore from 'nylas-store';
|
||||
import {remote} from 'electron';
|
||||
|
||||
import {LegacyEdgehillAPI} from 'nylas-exports';
|
||||
import {makeRequest} from '../nylas-api-request';
|
||||
|
||||
const autoUpdater = remote.getGlobal('application').autoUpdateManager;
|
||||
const preferredChannel = autoUpdater.preferredChannel;
|
||||
|
@ -29,41 +28,44 @@ class UpdateChannelStore extends NylasStore {
|
|||
return this._available;
|
||||
}
|
||||
|
||||
refreshChannel() {
|
||||
async refreshChannel() {
|
||||
// TODO BG
|
||||
return;
|
||||
LegacyEdgehillAPI.makeRequest({
|
||||
method: 'GET',
|
||||
path: `/update-channel`,
|
||||
qs: Object.assign({preferredChannel: preferredChannel}, autoUpdater.parameters()),
|
||||
json: true,
|
||||
}).run()
|
||||
.then(({current, available} = {}) => {
|
||||
try {
|
||||
const {current, available} = await makeRequest({
|
||||
server: 'identity',
|
||||
method: 'GET',
|
||||
path: `/api/update-channel`,
|
||||
qs: Object.assign({preferredChannel: preferredChannel}, autoUpdater.parameters()),
|
||||
json: true,
|
||||
});
|
||||
this._current = current || {name: "Edgehill API Not Available"};
|
||||
this._available = available || [];
|
||||
this.trigger();
|
||||
});
|
||||
return null;
|
||||
} catch (err) {
|
||||
// silent
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
setChannel(channelName) {
|
||||
LegacyEdgehillAPI.makeRequest({
|
||||
method: 'POST',
|
||||
path: `/update-channel`,
|
||||
qs: Object.assign({
|
||||
channel: channelName,
|
||||
preferredChannel: preferredChannel,
|
||||
}, autoUpdater.parameters()),
|
||||
json: true,
|
||||
}).run()
|
||||
.then(({current, available} = {}) => {
|
||||
async setChannel(channelName) {
|
||||
try {
|
||||
const {current, available} = await makeRequest({
|
||||
server: 'identity',
|
||||
method: 'POST',
|
||||
path: `/api/update-channel`,
|
||||
qs: Object.assign({
|
||||
channel: channelName,
|
||||
preferredChannel: preferredChannel,
|
||||
}, autoUpdater.parameters()),
|
||||
json: true,
|
||||
});
|
||||
this._current = current || {name: "Edgehill API Not Available"};
|
||||
this._available = available || [];
|
||||
this.trigger();
|
||||
}).catch((err) => {
|
||||
} catch (err) {
|
||||
NylasEnv.showErrorDialog(err.toString())
|
||||
this.trigger();
|
||||
});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import Task from './task';
|
||||
import NylasAPI from '../nylas-api'
|
||||
import {makeRequest, PermanentErrorCodes} from '../nylas-api-request'
|
||||
import {APIError} from '../errors'
|
||||
import IdentityStore from '../stores/identity-store'
|
||||
|
||||
|
@ -23,20 +23,20 @@ export default class SendFeatureUsageEventTask extends Task {
|
|||
}
|
||||
|
||||
async performRemote() {
|
||||
const body = new FormData();
|
||||
body.append('feature_name', this.featureName);
|
||||
const options = {
|
||||
method: 'POST',
|
||||
path: `/api/feature_usage_event`,
|
||||
body: body,
|
||||
};
|
||||
try {
|
||||
const updatedIdentity = await IdentityStore.nylasIDRequest(options);
|
||||
const updatedIdentity = await makeRequest({
|
||||
server: 'identity',
|
||||
method: 'POST',
|
||||
path: `/api/feature_usage_event`,
|
||||
body: {
|
||||
feature_name: this.featureName,
|
||||
},
|
||||
});
|
||||
await IdentityStore.saveIdentity(updatedIdentity);
|
||||
return Promise.resolve(Task.Status.Success)
|
||||
return Task.Status.Success;
|
||||
} catch (err) {
|
||||
if (err instanceof APIError) {
|
||||
if (NylasAPI.PermanentErrorCodes.includes(err.statusCode)) {
|
||||
if (PermanentErrorCodes.includes(err.statusCode)) {
|
||||
this.revert()
|
||||
return Promise.resolve([Task.Status.Failed, err])
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import SyncbackModelTask from './syncback-model-task'
|
||||
import DatabaseObjectRegistry from '../../registries/database-object-registry'
|
||||
import N1CloudAPI from '../../n1-cloud-api'
|
||||
import NylasAPIRequest from '../nylas-api-request'
|
||||
import {makeRequest} from '../nylas-api-request'
|
||||
|
||||
export default class SyncbackMetadataTask extends SyncbackModelTask {
|
||||
|
||||
|
@ -38,10 +38,10 @@ export default class SyncbackMetadataTask extends SyncbackModelTask {
|
|||
messageIds: messageIds,
|
||||
},
|
||||
};
|
||||
return new NylasAPIRequest({
|
||||
return makeRequest({
|
||||
api: N1CloudAPI,
|
||||
options,
|
||||
}).run()
|
||||
});
|
||||
}
|
||||
|
||||
applyRemoteChangesToModel = (model, {version}) => {
|
||||
|
|
|
@ -3,7 +3,7 @@ import _ from 'underscore';
|
|||
import Model from '../models/model';
|
||||
import Attributes from '../attributes';
|
||||
import {generateTempId} from '../models/utils';
|
||||
import {PermanentErrorCodes} from '../nylas-api';
|
||||
import {PermanentErrorCodes} from '../nylas-api-request';
|
||||
import {APIError} from '../errors';
|
||||
|
||||
const Status = {
|
||||
|
|
|
@ -51,10 +51,7 @@ const lazyLoadAndRegisterTask = (klassName, path) => {
|
|||
lazyLoad(`Actions`, 'flux/actions');
|
||||
|
||||
// API Endpoints
|
||||
lazyLoad(`NylasAPI`, 'flux/nylas-api');
|
||||
lazyLoad(`N1CloudAPI`, 'n1-cloud-api');
|
||||
lazyLoad(`EdgehillAPI`, 'flux/edgehill-api');
|
||||
lazyLoad(`LegacyEdgehillAPI`, 'flux/legacy-edgehill-api');
|
||||
lazyLoad(`NylasAPIHelpers`, 'flux/nylas-api-helpers');
|
||||
lazyLoad(`NylasAPIRequest`, 'flux/nylas-api-request');
|
||||
lazyLoad(`MailsyncProcess`, 'mailsync-process');
|
||||
|
|
Loading…
Reference in a new issue