From 0371f87a7d682486c45df2dca198ed64fdac3bab Mon Sep 17 00:00:00 2001 From: Mallory Allen Date: Fri, 2 Mar 2018 11:20:40 -0500 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20add=20accessMode=20option=20to=20co?= =?UTF-8?q?nfig=20and=20generate=20redirect=20urls=20based=20on=20it?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.js | 11 ++++--- routes/shopifyAuth.js | 17 ++++++++--- .../__snapshots__/shopifyAuth.test.js.snap | 2 +- routes/tests/shopifyAuth.test.js | 29 ++++++++++++++----- tests/ShopifyConfig.test.js | 5 ++-- .../__snapshots__/ShopifyConfig.test.js.snap | 3 ++ 6 files changed, 48 insertions(+), 19 deletions(-) diff --git a/index.js b/index.js index c894023..9fe156f 100644 --- a/index.js +++ b/index.js @@ -10,15 +10,18 @@ const ShopifyConfigTypes = { scope: PropTypes.arrayOf(PropTypes.string).isRequired, afterAuth: PropTypes.func.isRequired, shopStore: PropTypes.object, + accessMode: PropTypes.oneOf(['offline', 'online']), +}; + +const defaults = { + shopStore: new MemoryStrategy(), + accessMode: 'offline' }; module.exports = function shopify(shopifyConfig) { PropTypes.checkPropTypes(ShopifyConfigTypes, shopifyConfig, 'option', 'ShopifyExpress'); - const config = Object.assign( - {shopStore: new MemoryStrategy()}, - shopifyConfig, - ); + const config = Object.assign({}, defaults, shopifyConfig); return { middleware: createMiddleware(config), diff --git a/routes/shopifyAuth.js b/routes/shopifyAuth.js index aa5f1c3..d282a7e 100644 --- a/routes/shopifyAuth.js +++ b/routes/shopifyAuth.js @@ -9,6 +9,7 @@ module.exports = function createShopifyAuthRoutes({ scope, afterAuth, shopStore, + accessMode, }) { return { // This function initializes the Shopify OAuth Process @@ -21,16 +22,24 @@ module.exports = function createShopifyAuthRoutes({ } const redirectTo = `https://${shop}/admin/oauth/authorize`; - const redirectParams = - `?client_id=${apiKey}&scope=${scope}&redirect_uri=${host}` + - `${baseUrl}/callback`; + + const redirectParams = { + baseUrl, + scope, + client_id: apiKey, + redirect_uri: `${host}${baseUrl}/callback`, + }; + + if (accessMode === 'online') { + redirectParams['grant_options[]'] = 'per-user'; + } response.send( ` `, diff --git a/routes/tests/__snapshots__/shopifyAuth.test.js.snap b/routes/tests/__snapshots__/shopifyAuth.test.js.snap index 82bb90a..90f0a12 100644 --- a/routes/tests/__snapshots__/shopifyAuth.test.js.snap +++ b/routes/tests/__snapshots__/shopifyAuth.test.js.snap @@ -5,7 +5,7 @@ exports[`shopifyAuth / responds to get requests by returning a redirect page 1`] " diff --git a/routes/tests/shopifyAuth.test.js b/routes/tests/shopifyAuth.test.js index 49cdb06..adc0b6c 100644 --- a/routes/tests/shopifyAuth.test.js +++ b/routes/tests/shopifyAuth.test.js @@ -10,12 +10,11 @@ const PORT = 3000; const BASE_URL = `http://localhost:${PORT}` let server; -let afterAuthSpy; +let afterAuth; describe('shopifyAuth', async () => { beforeEach(async () => { - afterAuthSpy = jest.fn(); - - server = await createServer(afterAuthSpy); + afterAuth = jest.fn(); + server = await createServer({afterAuth}); }); afterEach(() => { @@ -31,6 +30,17 @@ describe('shopifyAuth', async () => { expect(data).toMatchSnapshot(); }); + it('redirect page includes per-user grant for accessMode: online', async () => { + await server.close(); + server = await createServer({accessMode: 'online'}); + + const response = await fetch(`${BASE_URL}/auth?shop=shop1`); + const data = await response.text(); + + expect(response.status).toBe(200); + expect(data).toContain('grant_options%5B%5D=per-user'); + }); + it('responds with a 400 when no shop query parameter is given', async () => { const response = await fetch(`${BASE_URL}/auth`); const data = await response.text(); @@ -59,17 +69,20 @@ describe('shopifyAuth', async () => { }); }); -function createServer(afterAuth) { +function createServer(userConfig = {}) { const app = express(); - const {auth, callback} = createShopifyAuthRoutes({ + const serverConfig = { host: 'http://myshop.myshopify.com', apiKey: 'key', secret: 'secret', scope: ['scope'], shopStore: new MemoryStrategy(), - afterAuth, - }); + accessMode: 'offline', + afterAuth: jest.fn(), + }; + + const {auth, callback} = createShopifyAuthRoutes(Object.assign({}, serverConfig, userConfig)); app.use('/auth', auth); app.use('/auth/callback', callback); diff --git a/tests/ShopifyConfig.test.js b/tests/ShopifyConfig.test.js index c7113ec..3cd4360 100644 --- a/tests/ShopifyConfig.test.js +++ b/tests/ShopifyConfig.test.js @@ -17,17 +17,18 @@ describe('ShopifyConfig', async () => { }); it('logs errors when given bad props', () => { - shopifyExpress({apiKey: 32, + shopifyExpress({ + apiKey: 32, host: { notGood: true }, secret: true, scope: 'orders', afterAuth: true, + accessMode: 'gerblable', }); expect(console.error).toBeCalled(); expect(console.error.mock.calls).toMatchSnapshot(); }); - it('does not log errors when given valid proptypes', () => { shopifyExpress({ apiKey: 'fake', diff --git a/tests/__snapshots__/ShopifyConfig.test.js.snap b/tests/__snapshots__/ShopifyConfig.test.js.snap index 08715f6..45b1f02 100644 --- a/tests/__snapshots__/ShopifyConfig.test.js.snap +++ b/tests/__snapshots__/ShopifyConfig.test.js.snap @@ -17,6 +17,9 @@ Array [ Array [ "Warning: Failed option type: Invalid option \`afterAuth\` of type \`boolean\` supplied to \`ShopifyExpress\`, expected \`function\`.", ], + Array [ + "Warning: Failed option type: Invalid option \`accessMode\` of value \`gerblable\` supplied to \`ShopifyExpress\`, expected one of [\\"offline\\",\\"online\\"].", + ], ] `;