From c877489429183e201ecbe5d731e57b6750729ad6 Mon Sep 17 00:00:00 2001 From: David Goemans Date: Mon, 28 Dec 2020 17:34:20 +0100 Subject: [PATCH] Authorization and errors --- .../__snapshots__/command-query.test.ts.snap | 7 +---- __tests__/command-query.test.ts | 28 +++++++++++++++++++ src/fetch-middleware.ts | 14 ++++++++-- src/index.ts | 4 +-- src/types.ts | 1 + 5 files changed, 43 insertions(+), 11 deletions(-) diff --git a/__tests__/__snapshots__/command-query.test.ts.snap b/__tests__/__snapshots__/command-query.test.ts.snap index 9bd8408..28bef70 100644 --- a/__tests__/__snapshots__/command-query.test.ts.snap +++ b/__tests__/__snapshots__/command-query.test.ts.snap @@ -1,8 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`spot Adds errors when the fetch fails 1`] = ` -Object { - "message": "invalid json response body at reason: Unexpected token < in JSON at position 0", - "type": "invalid-json", -} -`; +exports[`spot Adds errors when the fetch fails 1`] = `"FetchError: invalid json response body at reason: Unexpected token < in JSON at position 0"`; diff --git a/__tests__/command-query.test.ts b/__tests__/command-query.test.ts index cf16e0c..9df5dd8 100644 --- a/__tests__/command-query.test.ts +++ b/__tests__/command-query.test.ts @@ -25,6 +25,7 @@ interface DataType { let users: { [k: string]: User } = {}; const baseUrl = 'http://example.com'; +const authToken = 'AUTH_TOKEN'; const waitForLoadingDone = (spot: Spot) => new Promise(spot.subscribeOnce); @@ -79,6 +80,17 @@ describe('spot', () => { status: 200, body: '', }; + } if (req.url.startsWith(`${baseUrl}/authorized-endpoint`)) { + if (req.headers.get('authorization') === authToken) { + return { + body: '{}', + status: 200, + }; + } + return { + body: 'NOT AUTHORIZED', + status: 401, + }; } return { status: 404, @@ -224,4 +236,20 @@ describe('spot', () => { expect(spot.data.users['id-two']).toMatchObject({ age: 3, name: 'Rufus', role: 'Home Security' }); expect(spot.data.users['id-one']).toMatchObject({ age: 7, name: 'Spot', role: 'Good Boy' }); }); + + it('Inserts authorization headers data', async () => { + const spot = initializeSpot(baseUrl); + + await spot.query('authorized-endpoint', {}, ['auth'], { authorization: 'WRONG TOKEN' }); + expect(spot.errors).toHaveLength(1); + expect(spot.errors[0]).toBe('Error: QUERY FAILED 401: Unauthorized'); + + await spot.query('authorized-endpoint', {}, ['auth'], { authorization: authToken }); + const successResult = { + auth: {}, + loading: false, + }; + + expect(spot.data).toMatchObject(successResult); + }); }); diff --git a/src/fetch-middleware.ts b/src/fetch-middleware.ts index f8cf9c7..a08fb7f 100644 --- a/src/fetch-middleware.ts +++ b/src/fetch-middleware.ts @@ -29,6 +29,10 @@ export const fetchMiddleware = (api: MiddlewareAPI) => (next: Dispatch) => async const response = await fetch(url, { ...defaultFetchConfig, method: action?.config?.method ?? 'GET', + headers: { + ...defaultFetchConfig.headers, + authorization: action.config?.authorization || '', + }, }); if (response.status < 200 || response.status >= 400) { @@ -56,10 +60,10 @@ export const fetchMiddleware = (api: MiddlewareAPI) => (next: Dispatch) => async correlationId, }, }); - } catch (e) { + } catch (err) { // eslint-disable-next-line no-console - console.error(e); - api.dispatch({ type: 'ERROR', payload: e, metadata: { correlationId } }); + console.error(err); + api.dispatch({ type: 'ERROR', payload: err.toString(), metadata: { correlationId } }); } finally { api.dispatch({ type: 'STATE_UPDATED', metadata: { correlationId } }); } @@ -76,6 +80,10 @@ export const fetchMiddleware = (api: MiddlewareAPI) => (next: Dispatch) => async ...defaultFetchConfig, method: action?.config?.method ?? 'POST', body: JSON.stringify(action.payload.params), + headers: { + ...defaultFetchConfig.headers, + authorization: action.config?.authorization || '', + }, }); if (response.status < 200 || response.status >= 400) { diff --git a/src/index.ts b/src/index.ts index 67e2a32..b4b4af4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -56,7 +56,7 @@ export class Spot { await this.waitForQuery(); } - command = async (endpoint: string, params: { [k: string]: unknown }, config?: { method?: string }) => { + command = async (endpoint: string, params: { [k: string]: unknown }, config?: ActionConfig) => { this.store.dispatch({ type: 'COMMAND', payload: { params, endpoint }, @@ -103,7 +103,7 @@ export class Spot { } get errors() { - return deepmerge([], this.store.getState().errors); + return [...this.store.getState().errors]; } } diff --git a/src/types.ts b/src/types.ts index ca54132..0906f2e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -4,6 +4,7 @@ export type ActionType = 'SETUP' | 'ERROR' | 'QUERY' | 'COMMAND' | 'QUERY_COMPLE export interface ActionConfig { method?: string; + authorization?: string; } export interface Action {