Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'develop' of https://github.com/chrisdevwords/slack-spot…
…ify-lambda * 'develop' of https://github.com/chrisdevwords/slack-spotify-lambda: #19 wiring up the radio command #19 createRadioStation method works #19 adding playlist pending message to slack response #19 setting radio arn and access token direclty on radio module instead of passing from command module #19 adding a createRadio station spec #19 adding stub test env var for access token, radio lambda arn #19 parsing the response_url from the body, passing it to the command, stubbing out a radio command handler #19 setting accesToken in the index handler #19 adding a getTrackInfo method #19 setAccessToken method called from index handler, pulls from env var #19 adding a setAccessToken method to slack command module #19 adding a getTrackInfo method #19 adding extract from uri util #19 adding aws module for invoking other lambda functions #19 adding the aws sdk to dev dependencies
- Loading branch information
Showing
14 changed files
with
818 additions
and
9 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
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 | ||
const AWS = require('aws-sdk'); | ||
|
||
module.exports = { | ||
|
||
getLambdaClient(config = {}) { | ||
if (!this.lambdaClient) { | ||
this.lambdaClient = new AWS.Lambda(config) | ||
} | ||
return this.lambdaClient; | ||
}, | ||
|
||
invokeLambda(params) { | ||
const lambdaClient = this.getLambdaClient(); | ||
return new Promise((resolve, reject) => { | ||
lambdaClient.invoke(params, (err, data) => { | ||
if (err) { | ||
reject(err); | ||
} else { | ||
resolve(data); | ||
} | ||
}); | ||
}); | ||
} | ||
}; |
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,98 @@ | ||
const request = require('request-promise-native'); | ||
const { extractFromUri } = require('./util/parse'); | ||
const { PL_PENDING } = require('./slack-resp'); | ||
const aws = require('./aws'); | ||
|
||
|
||
const API_BASE = 'https://api.spotify.com/v1'; | ||
const ERROR_EXPIRED_TOKEN = 'Spotify User Access token ' + | ||
'is expired or invalid. ' + | ||
'Please check the Spotify host machine.'; | ||
const ERROR_INVALID_TRACK_URI = 'Please provide a uri ' + | ||
'for a valid Spotify track.'; | ||
const TRACK_ENDPOINT = id => | ||
`${API_BASE}/tracks/${id}`; | ||
|
||
let _functionARN; | ||
let _accessToken; | ||
|
||
module.exports = { | ||
|
||
ERROR_INVALID_TRACK_URI, | ||
ERROR_EXPIRED_TOKEN, | ||
|
||
setAccessToken(token) { | ||
_accessToken = token; | ||
}, | ||
|
||
setFunctionARN(functionARN) { | ||
_functionARN = functionARN | ||
}, | ||
|
||
handleStatusCodeError(err) { | ||
if (err.error) { | ||
let message; | ||
switch (err.statusCode) { | ||
case 404: | ||
case 400: | ||
message = ERROR_INVALID_TRACK_URI; | ||
break; | ||
case 401: | ||
message = ERROR_EXPIRED_TOKEN; | ||
break; | ||
default: | ||
message = err.error.error.message; | ||
} | ||
const error = Error(message); | ||
error.statusCode = err.statusCode; | ||
throw error; | ||
} | ||
return Promise.reject(err) | ||
}, | ||
|
||
getTrackInfo(trackId) { | ||
return request | ||
.get({ | ||
uri: TRACK_ENDPOINT(trackId), | ||
headers: { | ||
Authorization: `Bearer ${_accessToken}` | ||
}, | ||
json: true | ||
}) | ||
.then(({ artists, name, popularity }) => ({ | ||
name, | ||
artist: artists | ||
.map(a => a.name) | ||
.join(', '), | ||
artistIds: artists | ||
.map(a => a.id), | ||
popularity, | ||
id: trackId | ||
})) | ||
.catch(this.handleStatusCodeError); | ||
|
||
}, | ||
|
||
createRadioStation(text, responseUrl) { | ||
const trackId = extractFromUri(text, 'track'); | ||
return this | ||
.getTrackInfo(trackId) | ||
.then(trackInfo => | ||
aws.invokeLambda({ | ||
FunctionName: _functionARN, | ||
InvocationType: 'Event', | ||
LogType: 'Tail', | ||
Payload: JSON.stringify({ | ||
body: { | ||
track:trackInfo, | ||
response_url: responseUrl | ||
} | ||
}) | ||
}).then(() => PL_PENDING(trackInfo)) | ||
) | ||
.catch(({ message }) => Promise.reject({ | ||
message, | ||
statusCode: 200 | ||
})); | ||
} | ||
}; |
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 |
---|---|---|
@@ -1,2 +1,4 @@ | ||
SLACK_TOKEN=foo; | ||
SPOTIFY_LOCAL_URL=http://localhost:5000 | ||
RADIO_LAMBDA=arn:aws:lambda:us-east-1:0000000:function:testInvokeFunctioInvokee | ||
SPOTIFY_USER_ACCESS_TOKEN=foo |
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,47 @@ | ||
const { beforeEach, afterEach, describe, it } = require('mocha'); | ||
const { expect } = require('chai'); | ||
const aws = require('../../src/aws'); | ||
|
||
const context = describe; | ||
|
||
describe('The aws.getLambdaClient method', () => { | ||
|
||
context('with a config', () => { | ||
|
||
afterEach(() => { | ||
delete aws.lambdaClient; | ||
}); | ||
|
||
const awsConfig = { | ||
region: 'us-east-1' | ||
}; | ||
|
||
it('creates a lambda client for the module', (done) => { | ||
expect(aws.lambdaClient).to.be.undefined; | ||
const client = aws.getLambdaClient(awsConfig); | ||
expect(client).to.equal(aws.lambdaClient); | ||
expect(aws.lambdaClient).to.not.be.undefined; | ||
done(); | ||
}); | ||
}); | ||
|
||
context('without a config', () => { | ||
|
||
afterEach(() => { | ||
delete aws.lambdaClient; | ||
}); | ||
|
||
it('creates a lambda client for the module', (done) => { | ||
const client = aws.getLambdaClient(); | ||
expect(client).to.equal(aws.lambdaClient); | ||
done(); | ||
}); | ||
|
||
it('creates the lambda client only once ', (done) => { | ||
const client = aws.getLambdaClient(); | ||
const client2 = aws.getLambdaClient(); | ||
expect(client2).to.equal(client); | ||
done(); | ||
}); | ||
}); | ||
}); |
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,81 @@ | ||
const { beforeEach, afterEach, describe, it } = require('mocha'); | ||
const { expect, config } = require('chai'); | ||
const sinon = require('sinon'); | ||
const aws = require('../../src/aws'); | ||
|
||
const context = describe; | ||
|
||
describe('The aws.invokeLambda method', () => { | ||
context('when successful', () => { | ||
|
||
const successResp = { | ||
StatusCode: 202, | ||
Payload: '' | ||
}; | ||
|
||
beforeEach(() => { | ||
aws.getLambdaClient({ | ||
region: 'us-east-1' | ||
}); | ||
sinon.stub(aws.lambdaClient, 'invoke', (params, cb) => { | ||
cb(null, successResp); | ||
}); | ||
}); | ||
|
||
afterEach(() => { | ||
aws.lambdaClient.invoke.restore(); | ||
delete aws.lambdaClient; | ||
}); | ||
|
||
it('resolves a promise with response data', (done) => { | ||
const params = { | ||
FunctionName: 'arn:aws:lambda:us-east-1:0000000:function:testInvokeFunctioInvokee', | ||
InvocationType: 'Event', | ||
LogType: 'Tail' | ||
}; | ||
aws.invokeLambda(params) | ||
.then((resp) => { | ||
expect(resp).to.eq(successResp); | ||
done(); | ||
}) | ||
.catch(done); | ||
}); | ||
}); | ||
|
||
context('when unsuccessful', () => { | ||
|
||
const expectedError = new Error('Something happened.'); | ||
expectedError.code = 403; | ||
|
||
beforeEach(() => { | ||
aws.getLambdaClient({ | ||
region: 'us-east-1' | ||
}); | ||
sinon.stub(aws.lambdaClient, 'invoke', (params, cb) => { | ||
cb(expectedError); | ||
}); | ||
}); | ||
|
||
afterEach(() => { | ||
aws.lambdaClient.invoke.restore(); | ||
delete aws.lambdaClient; | ||
}); | ||
|
||
it('resolves a promise with response data', (done) => { | ||
const params = { | ||
FunctionName: 'arn:aws:lambda:us-east-1:000000000000:function:testInvokeFunctioInvokee', | ||
InvocationType: 'Event', | ||
LogType: 'Tail' | ||
}; | ||
aws.invokeLambda(params) | ||
.then(() => { | ||
done(Error('This promise should not resolve.')); | ||
}) | ||
.catch((err) => { | ||
expect(err).to.equal(expectedError); | ||
done(); | ||
}) | ||
.catch(done); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.