From 34eb53f72e8bb6f1f18a39ac6838991bbed86660 Mon Sep 17 00:00:00 2001 From: Sebastian Rindom Date: Mon, 21 Sep 2020 18:12:02 +0200 Subject: [PATCH] fix(medusa-plugin-brightpearl): automatic token refresh --- .../medusa-plugin-brightpearl/package.json | 2 + .../src/services/__tests__/brightpearl.js | 40 ++++++- .../src/services/brightpearl.js | 5 - .../src/utils/__mocks__/brightpearl.js | 108 +++++++++++++----- 4 files changed, 121 insertions(+), 34 deletions(-) diff --git a/packages/medusa-plugin-brightpearl/package.json b/packages/medusa-plugin-brightpearl/package.json index 5c86a6bba0..b136d3d3cb 100644 --- a/packages/medusa-plugin-brightpearl/package.json +++ b/packages/medusa-plugin-brightpearl/package.json @@ -21,6 +21,8 @@ "@babel/preset-env": "^7.7.5", "@babel/register": "^7.7.4", "@babel/runtime": "^7.9.6", + "axios-mock-adapter": "^1.18.2", + "axios-mock-server": "^0.19.0", "client-sessions": "^0.8.0", "cross-env": "^7.0.2", "eslint": "^6.8.0", diff --git a/packages/medusa-plugin-brightpearl/src/services/__tests__/brightpearl.js b/packages/medusa-plugin-brightpearl/src/services/__tests__/brightpearl.js index 45bac6472a..8d7c03fc5b 100644 --- a/packages/medusa-plugin-brightpearl/src/services/__tests__/brightpearl.js +++ b/packages/medusa-plugin-brightpearl/src/services/__tests__/brightpearl.js @@ -1,5 +1,6 @@ import BrightpearlService from "../brightpearl" -import Brightpearl, { mockCreateOrder } from "../../utils/brightpearl" +import { mockCreateOrder } from "../../utils/brightpearl" +import MockAdapter from "axios-mock-adapter" jest.mock("../../utils/brightpearl") @@ -46,6 +47,43 @@ const RegionService = { } describe("BrightpearlService", () => { + describe("getClient", () => { + it("creates client", async () => { + const oauth = { + refreshToken: () => { + return Promise.resolve({ + access_token: "good", + }) + }, + retrieveByName: () => { + return Promise.resolve({ + data: { + access_token: "bad", + }, + }) + }, + } + + const bpService = new BrightpearlService({ oauthService: oauth }, {}) + const client = await bpService.getClient() + + const mockServer = new MockAdapter(client.client) + + mockServer.onGet("/success").reply(() => { + return [200] + }) + mockServer.onGet("/fail").reply((req) => { + if (req.headers.Authorization === "Bearer good") { + return [200] + } + return [401] + }) + + await client.test.fail() + await client.test.fail() + }) + }) + describe("createSalesOrder", () => { const order = { items: [ diff --git a/packages/medusa-plugin-brightpearl/src/services/brightpearl.js b/packages/medusa-plugin-brightpearl/src/services/brightpearl.js index 67ca0231a8..cdc613859e 100644 --- a/packages/medusa-plugin-brightpearl/src/services/brightpearl.js +++ b/packages/medusa-plugin-brightpearl/src/services/brightpearl.js @@ -26,10 +26,6 @@ class BrightpearlService extends BaseService { } async getClient() { - if (this.brightpearlClient_) { - return this.brightpearlClient_ - } - const authData = await this.oauthService_.retrieveByName("brightpearl") const { data } = authData @@ -60,7 +56,6 @@ class BrightpearlService extends BaseService { ) this.authData_ = data - this.brightpearlClient_ = client return client } diff --git a/packages/medusa-plugin-brightpearl/src/utils/__mocks__/brightpearl.js b/packages/medusa-plugin-brightpearl/src/utils/__mocks__/brightpearl.js index ac9446c4c2..100831a4d0 100644 --- a/packages/medusa-plugin-brightpearl/src/utils/__mocks__/brightpearl.js +++ b/packages/medusa-plugin-brightpearl/src/utils/__mocks__/brightpearl.js @@ -1,37 +1,89 @@ +import axios from "axios" export const mockCreateOrder = jest .fn() .mockReturnValue(Promise.resolve("1234")) -const mock = jest.fn().mockImplementation(() => { - return { - warehouses: { - createReservation: jest.fn().mockReturnValue(Promise.resolve()), - }, - payments: { - create: jest.fn().mockReturnValue(Promise.resolve()), - }, - orders: { - create: mockCreateOrder, - retrieve: jest.fn().mockReturnValue(Promise.resolve()), - }, - products: { - retrieveBySKU: jest.fn().mockReturnValue( - Promise.resolve({ - productId: 1234, - }) - ), - }, - customers: { - retrieveByEmail: jest.fn().mockReturnValue( - Promise.resolve([ - { - primaryEmail: "test@example.com", - contactId: "12345", - }, - ]) - ), +const mock = jest.fn().mockImplementation(function (options, onRefresh) { + this.token_ = options.access_token + this.client = axios.create({ + baseURL: `https://mock.com`, + headers: { + "brightpearl-app-ref": "medusa-dev", + "brightpearl-dev-ref": "sebrindom", }, + }) + + this.updateAuth = (data) => { + this.token_ = data.access_token } + + this.client.interceptors.request.use((request) => { + const token = this.token_ + + if (token) { + request.headers["Authorization"] = `Bearer ${token}` + } + + return request + }) + + this.client.interceptors.response.use(undefined, async (error) => { + const response = error.response + + if (response) { + if ( + response.status === 401 && + error.config && + !error.config.__isRetryRequest + ) { + try { + await onRefresh(this) + } catch (authError) { + // refreshing has failed, but report the original error, i.e. 401 + return Promise.reject(error) + } + + // retry the original request + error.config.__isRetryRequest = true + return this.client(error.config) + } + } + + return Promise.reject(error) + }) + + this.test = { + success: () => this.client.get("/success"), + fail: () => this.client.get("/fail"), + } + this.warehouses = { + createReservation: jest.fn().mockReturnValue(Promise.resolve()), + } + this.payments = { + create: jest.fn().mockReturnValue(Promise.resolve()), + } + this.orders = { + create: mockCreateOrder, + retrieve: jest.fn().mockReturnValue(Promise.resolve()), + } + this.products = { + retrieveBySKU: jest.fn().mockReturnValue( + Promise.resolve({ + productId: 1234, + }) + ), + } + this.customers = { + retrieveByEmail: jest.fn().mockReturnValue( + Promise.resolve([ + { + primaryEmail: "test@example.com", + contactId: "12345", + }, + ]) + ), + } + return this }) export default mock