-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(twitter): EN-6897: Add Twitter features. (#88)
* Add functionality to save Twitter OAuth tokens. * Add functionality to retrieve connected Twitter usernames. Signed-off-by: Jeff Cuevas-Koch <[email protected]> Co-authored-by: Jeff Cuevas-Koch <[email protected]>
- Loading branch information
1 parent
9f19d27
commit 79b7dfe
Showing
9 changed files
with
288 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import chai from 'chai'; | ||
import chaiAsPromised from 'chai-as-promised'; | ||
import fetchMock from 'fetch-mock'; | ||
import Track from '../index'; | ||
import { | ||
charlie, | ||
twitter as mockTwitter, | ||
} from '../mocks'; | ||
|
||
chai.should(); | ||
chai.use(chaiAsPromised); | ||
|
||
describe('When saving a twitter oauth token', () => { | ||
const api = new Track({ autoRenew: false }); | ||
|
||
beforeEach(() => charlie.setUpSuccessfulMock(api.client)); | ||
beforeEach(() => mockTwitter.setUpSuccessfulMock(api.client)); | ||
beforeEach(() => fetchMock.catch(503)); | ||
afterEach(fetchMock.restore); | ||
|
||
it('should save a token', () => { | ||
api.logIn({ username: '[email protected]', password: 'securepassword' }); | ||
|
||
const tokenPromise = api.customer('SYNC').twitterOAuth({ | ||
username: 'GMVSyncromatics', | ||
token: 'example_oauth_token', | ||
secret: 'example_oauth_secret', | ||
profile_image_url: 'https://example.com/gmvsyncromatics.png', | ||
}).update().then(success => success); // check success | ||
|
||
return tokenPromise; | ||
}); | ||
}); | ||
|
||
describe('When retrieving the connected twitter username', () => { | ||
const api = new Track({ autoRenew: false }); | ||
|
||
beforeEach(() => charlie.setUpSuccessfulMock(api.client)); | ||
beforeEach(() => mockTwitter.setUpSuccessfulMock(api.client)); | ||
beforeEach(() => fetchMock.catch(503)); | ||
afterEach(fetchMock.restore); | ||
|
||
it('should save a token', () => { | ||
api.logIn({ username: '[email protected]', password: 'securepassword' }); | ||
|
||
const usernamePromise = api.customer('SYNC').twitterUsername() | ||
.fetch() | ||
.then(username => username); // Do things with username | ||
|
||
return usernamePromise; | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
// eslint-disable-next-line import/no-extraneous-dependencies | ||
import fetchMock from 'fetch-mock'; | ||
import Client from '../Client'; | ||
|
||
const twitter = { | ||
setUpSuccessfulMock: (client) => { | ||
const oauthUrl = '/1/SYNC/twitter/oauth'; | ||
const oauthMutationResponse = () => new Response(undefined); | ||
|
||
const usernameUrl = '/1/SYNC/twitter/username'; | ||
const usernameResponse = () => new Response(Client.toBlob({ | ||
href: '/1/SYNC/twitter/username', | ||
username: 'GMVSYNC', | ||
is_valid: true, | ||
profile_image_url: 'https://example.com/gmvsync.png', | ||
})); | ||
|
||
fetchMock | ||
.get(client.resolve(usernameUrl), usernameResponse) | ||
.put(client.resolve(oauthUrl), oauthMutationResponse) | ||
.delete(client.resolve(oauthUrl), oauthMutationResponse); | ||
}, | ||
}; | ||
|
||
export default twitter; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import Resource from './Resource'; | ||
|
||
/** | ||
* Write-only Twitter OAuth resource | ||
*/ | ||
class TwitterOAuth extends Resource { | ||
/** | ||
* Creates a new TwitterOAuth | ||
* | ||
* Will populate itself with the values given after the client parameter | ||
* @param {Client} client Instance of pre-configured client | ||
* @param {Object} rest The object to use in assigning values to this instance | ||
*/ | ||
constructor(client, rest) { | ||
super(client); | ||
const { code, ...newProperties } = rest; | ||
this.customerCode = code; | ||
Object.assign(this, newProperties, { | ||
hydrated: false, | ||
}); | ||
} | ||
|
||
/** | ||
* Makes a href for a given customer code | ||
* @param {string} customerCode Customer code | ||
* @returns {string} URI to instance of TwitterOAuth | ||
*/ | ||
static makeHref(customerCode) { | ||
return { | ||
href: `/1/${customerCode}/twitter/oauth`, | ||
code: customerCode, | ||
}; | ||
} | ||
|
||
/** | ||
* Saves data for this Twitter OAuth to the server | ||
* | ||
* Does not return the created object since TwitterOAuth | ||
* is meant to be write-only. | ||
* @returns {Promise} If successful, returns a completed promise. | ||
*/ | ||
update() { | ||
const { client, customerCode, hydrated, ...body } = this; | ||
const { href } = TwitterOAuth.makeHref(customerCode); | ||
return client.put(href, { body }).then(() => ({ success: true })); | ||
} | ||
|
||
/** | ||
* Removes stored Twitter OAuth information via the client | ||
* @returns {Promise} If successful, returns a resolved promise | ||
*/ | ||
delete() { | ||
const { client, customerCode } = this; | ||
const { href } = TwitterOAuth.makeHref(customerCode); | ||
return client.delete(href).then(() => {}); | ||
} | ||
} | ||
|
||
export default TwitterOAuth; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import chai from 'chai'; | ||
import chaiAsPromised from 'chai-as-promised'; | ||
import fetchMock from 'fetch-mock'; | ||
|
||
import Client from '../Client'; | ||
import TwitterOAuth from './TwitterOAuth'; | ||
import { twitter as mockTwitter } from '../mocks'; | ||
|
||
chai.should(); | ||
chai.use(chaiAsPromised); | ||
|
||
describe('When instantiating a TwitterOAuth based on customer', () => { | ||
const client = new Client(); | ||
const oauth = new TwitterOAuth(client, TwitterOAuth.makeHref('SYNC')); | ||
|
||
it('should set the href', () => oauth.href.should.equal('/1/SYNC/twitter/oauth')); | ||
it('should not be hydrated', () => oauth.hydrated.should.equal(false)); | ||
}); | ||
|
||
describe('When updating an TwitterOAuth information for a customer', () => { | ||
const client = new Client(); | ||
|
||
beforeEach(() => mockTwitter.setUpSuccessfulMock(client)); | ||
beforeEach(() => fetchMock.catch(503)); | ||
afterEach(fetchMock.restore); | ||
|
||
let promise; | ||
beforeEach(() => { | ||
const oauth = new TwitterOAuth(client, { | ||
code: 'SYNC', | ||
username: 'GMVSyncromatics', | ||
token: 'example_oauth_token', | ||
secret: 'example_oauth_secret', | ||
profile_image_url: 'https://example.com/gmvsyncromatics.png', | ||
}); | ||
promise = oauth.update().then(updated => updated); | ||
}); | ||
|
||
it('should resolve the promise', () => promise.should.be.fulfilled); | ||
it('should return success', () => promise.then(v => v.success).should.eventually.equal(true)); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import Resource from './Resource'; | ||
|
||
/** | ||
* Read-only Twitter Username resource. | ||
*/ | ||
class TwitterUsername extends Resource { | ||
/** | ||
* Creates a new TwitterUsername | ||
* | ||
* @param {Client} client Instance of pre-configured client | ||
* @param {Object} rest The object to use in assigning values to this instance | ||
*/ | ||
constructor(client, rest) { | ||
super(client); | ||
const { code, ...newProperties } = rest; | ||
this.customerCode = code; | ||
const hydrated = !Object.keys(newProperties).every(k => k === 'href' || k === 'customerCode'); | ||
Object.assign(this, newProperties, { | ||
hydrated, | ||
}); | ||
} | ||
|
||
/** | ||
* Makes a href for a given customer code | ||
* @param {string} customerCode Customer code | ||
* @returns {string} URI to instance of call | ||
*/ | ||
static makeHref(customerCode) { | ||
return { | ||
code: customerCode, | ||
href: `/1/${customerCode}/twitter/username`, | ||
}; | ||
} | ||
|
||
/** | ||
* Fetches the data for this TwitterUsername via the client | ||
* @returns {Promise} If successful, a hydrated instance of this TwitterUsername | ||
*/ | ||
fetch() { | ||
const { customerCode } = this; | ||
const { href } = TwitterUsername.makeHref(customerCode); | ||
return this.client.get(href) | ||
.then(response => response.json()) | ||
.then(username => new TwitterUsername(this.client, { ...this, ...username })); | ||
} | ||
} | ||
|
||
export default TwitterUsername; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import chai from 'chai'; | ||
import chaiAsPromised from 'chai-as-promised'; | ||
import fetchMock from 'fetch-mock'; | ||
|
||
import Client from '../Client'; | ||
import TwitterUsername from './TwitterUsername'; | ||
import { twitter as mockTwitter } from '../mocks'; | ||
|
||
chai.should(); | ||
chai.use(chaiAsPromised); | ||
|
||
describe('When instantiating a TwitterUsername based on customer', () => { | ||
const client = new Client(); | ||
const oauth = new TwitterUsername(client, TwitterUsername.makeHref('SYNC')); | ||
|
||
it('should set the href', () => oauth.href.should.equal('/1/SYNC/twitter/username')); | ||
it('should not be hydrated', () => oauth.hydrated.should.equal(false)); | ||
}); | ||
|
||
describe('When fetching a TwitterUsername for a customer', () => { | ||
const client = new Client(); | ||
|
||
beforeEach(() => mockTwitter.setUpSuccessfulMock(client)); | ||
beforeEach(() => fetchMock.catch(503)); | ||
afterEach(fetchMock.restore); | ||
|
||
let promise; | ||
beforeEach(() => { | ||
promise = new TwitterUsername(client, TwitterUsername.makeHref('SYNC')) | ||
.fetch() | ||
.then(username => username); | ||
}); | ||
|
||
it('should resolve the promise', () => promise.should.be.fulfilled); | ||
it('should set the href', () => promise.then(v => v.href).should.eventually.equal('/1/SYNC/twitter/username')); | ||
it('should set the username', () => promise.then(v => v.username).should.eventually.equal('GMVSYNC')); | ||
it('should set is_valid', () => promise.then(v => v.is_valid).should.eventually.equal(true)); | ||
it('should set the profile image url', () => promise.then(v => v.profile_image_url).should.eventually.equal('https://example.com/gmvsync.png')); | ||
it('should be hydrated', () => promise.then(v => v.hydrated).should.eventually.equal(true)); | ||
}); |