Skip to content
This repository has been archived by the owner on Jul 14, 2022. It is now read-only.

Commit

Permalink
✨ add accessMode option to config and generate redirect urls based on it
Browse files Browse the repository at this point in the history
  • Loading branch information
marutypes committed Mar 2, 2018
1 parent 482175f commit 0371f87
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 19 deletions.
11 changes: 7 additions & 4 deletions index.js
Expand Up @@ -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),
Expand Down
17 changes: 13 additions & 4 deletions routes/shopifyAuth.js
Expand Up @@ -9,6 +9,7 @@ module.exports = function createShopifyAuthRoutes({
scope,
afterAuth,
shopStore,
accessMode,
}) {
return {
// This function initializes the Shopify OAuth Process
Expand All @@ -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(
`<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
window.top.location.href = "${redirectTo}${redirectParams}"
window.top.location.href = "${redirectTo}${querystring.stringify(redirectParams)}"
</script>
</head>
</html>`,
Expand Down
2 changes: 1 addition & 1 deletion routes/tests/__snapshots__/shopifyAuth.test.js.snap
Expand Up @@ -5,7 +5,7 @@ exports[`shopifyAuth / responds to get requests by returning a redirect page 1`]
<html>
<head>
<script type=\\"text/javascript\\">
window.top.location.href = \\"https://shop1/admin/oauth/authorize?client_id=key&scope=scope&redirect_uri=http://myshop.myshopify.com/auth/callback\\"
window.top.location.href = \\"https://shop1/admin/oauth/authorizebaseUrl=%2Fauth&scope=scope&client_id=key&redirect_uri=http%3A%2F%2Fmyshop.myshopify.com%2Fauth%2Fcallback\\"
</script>
</head>
</html>"
Expand Down
29 changes: 21 additions & 8 deletions routes/tests/shopifyAuth.test.js
Expand Up @@ -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(() => {
Expand All @@ -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();
Expand Down Expand Up @@ -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);
Expand Down
5 changes: 3 additions & 2 deletions tests/ShopifyConfig.test.js
Expand Up @@ -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',
Expand Down
3 changes: 3 additions & 0 deletions tests/__snapshots__/ShopifyConfig.test.js.snap
Expand Up @@ -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\\"].",
],
]
`;

Expand Down

0 comments on commit 0371f87

Please sign in to comment.