diff --git a/packages/data-connect/src/network/fetch.ts b/packages/data-connect/src/network/fetch.ts index 831bc6cc603..eeda528840e 100644 --- a/packages/data-connect/src/network/fetch.ts +++ b/packages/data-connect/src/network/fetch.ts @@ -64,14 +64,15 @@ export function dcFetch( } catch (e) { throw new DataConnectError(Code.OTHER, JSON.stringify(e)); } + const message = getMessage(jsonResponse); if (response.status >= 400) { logError( 'Error while performing request: ' + JSON.stringify(jsonResponse) ); if(response.status === 401) { - throw new DataConnectError(Code.UNAUTHORIZED, JSON.stringify(jsonResponse)); + throw new DataConnectError(Code.UNAUTHORIZED, message); } - throw new DataConnectError(Code.OTHER, JSON.stringify(jsonResponse)); + throw new DataConnectError(Code.OTHER, message); } return jsonResponse; }) @@ -84,3 +85,13 @@ export function dcFetch( return res as { data: T; errors: Error[] }; }); } +interface MessageObject { + message?: string; +} +function getMessage(obj: MessageObject) : string { + if ('message' in obj) { + console.log('message') + return obj.message; + } + return JSON.stringify(obj); +} diff --git a/packages/data-connect/test/post.gql b/packages/data-connect/test/post.gql index 79bd6688efe..d483ec10130 100644 --- a/packages/data-connect/test/post.gql +++ b/packages/data-connect/test/post.gql @@ -9,4 +9,10 @@ query listPosts @auth(level: PUBLIC) { content } } +query listPosts2 { + posts { + id, + content + } +} diff --git a/packages/data-connect/test/queries.test.ts b/packages/data-connect/test/queries.test.ts index d84279d014f..50aeab1c86a 100644 --- a/packages/data-connect/test/queries.test.ts +++ b/packages/data-connect/test/queries.test.ts @@ -59,6 +59,7 @@ const SEEDED_DATA = [ content: 'task 2' } ]; +const REAL_DATA = SEEDED_DATA.map(obj => ({ ...obj, id: obj.id.replace(/-/g, '')})) function seedDatabase(instance: DataConnect): Promise { // call mutation query that adds SEEDED_DATA to database return new Promise((resolve, reject) => { @@ -100,7 +101,7 @@ describe('DataConnect Tests', async () => { const taskListQuery = queryRef(dc, 'listPosts'); const taskListRes = await executeQuery(taskListQuery); expect(taskListRes.data).to.deep.eq({ - posts: SEEDED_DATA + posts: REAL_DATA }); }); it(`instantly executes a query if one hasn't been subscribed to`, async () => { @@ -121,7 +122,7 @@ describe('DataConnect Tests', async () => { ); const res = await promise; expect(res.data).to.deep.eq({ - posts: SEEDED_DATA + posts: REAL_DATA }); expect(res.source).to.eq(SOURCE_SERVER); }); @@ -138,7 +139,7 @@ describe('DataConnect Tests', async () => { const result = await waitForFirstEvent(taskListQuery); const serializedRef: SerializedRef = { data: { - posts: SEEDED_DATA + posts: REAL_DATA }, fetchTime: Date.now().toLocaleString(), refInfo: { @@ -149,7 +150,7 @@ describe('DataConnect Tests', async () => { name: taskListQuery.name, variables: undefined }, - source: SOURCE_SERVER + source: SOURCE_CACHE }; expect(result.toJSON()).to.deep.eq(serializedRef); expect(result.source).to.deep.eq(SOURCE_CACHE); @@ -163,10 +164,15 @@ describe('DataConnect Tests', async () => { }); connectDataConnectEmulator(fakeInstance, 'localhost', Number(0)); const taskListQuery = queryRef(dc, 'listPosts'); - expect(await executeQuery(taskListQuery)).to.eventually.be.rejectedWith( + expect(executeQuery(taskListQuery)).to.eventually.be.rejectedWith( 'ECONNREFUSED' ); }); + it('throws an error with just the message when the server responds with an error', async () => { + const invalidTaskListQuery = queryRef(dc, 'listPosts2'); + const message = 'unauthorized: you are not authorized to perform this operation'; + await expect(executeQuery(invalidTaskListQuery)).to.eventually.be.rejectedWith(message); + }) }); async function waitForFirstEvent( query: QueryRef diff --git a/packages/data-connect/test/unit/dataconnect.test.ts b/packages/data-connect/test/unit/dataconnect.test.ts index ba202a0b1ca..620e39f5ae6 100644 --- a/packages/data-connect/test/unit/dataconnect.test.ts +++ b/packages/data-connect/test/unit/dataconnect.test.ts @@ -4,8 +4,8 @@ import { expect } from "chai"; describe('Data Connect Test', () => { it('should throw an error if `projectId` is not provided', () => { - const app = initializeApp({}); - expect(() => getDataConnect({ connector: 'c', location: 'l', service: 's'})).to.throw('Project ID must be provided. Did you pass in a proper projectId to initializeApp?'); + const app = initializeApp({projectId: undefined}, 'a'); + expect(() => getDataConnect(app,{ connector: 'c', location: 'l', service: 's'})).to.throw('Project ID must be provided. Did you pass in a proper projectId to initializeApp?'); deleteApp(app); }); it('should not throw an error if `projectId` is provided', () => { diff --git a/packages/data-connect/test/unit/fetch.test.ts b/packages/data-connect/test/unit/fetch.test.ts new file mode 100644 index 00000000000..44875017705 --- /dev/null +++ b/packages/data-connect/test/unit/fetch.test.ts @@ -0,0 +1,39 @@ +import { expect, use } from 'chai'; +import { dcFetch, initializeFetch } from '../../src/network/fetch'; +import chaiAsPromised from 'chai-as-promised'; +import * as sinon from 'sinon'; +use(chaiAsPromised); +function mockFetch(json: any) { + const fakeFetchImpl = sinon.stub().returns( + Promise.resolve({ + json: () => { + return Promise.resolve(json); + }, + status: 401 + } as Response) + ); + initializeFetch(fakeFetchImpl); +} +describe('fetch', () => { + it('should throw an error with just the message when the server responds with an error with a message property in the body', async () => { + const message = 'Failed to connect to Postgres instance'; + mockFetch({ + code: 401, + message + }); + await expect( + dcFetch('http://localhost', {}, {} as AbortController, null) + ).to.eventually.be.rejectedWith(message); + }); + it('should throw a stringified message when the server responds with an error without a message property in the body', async () => { + const message = 'Failed to connect to Postgres instance'; + const json = { + code: 401, + message1: message + } + mockFetch(json); + await expect( + dcFetch('http://localhost', {}, {} as AbortController, null) + ).to.eventually.be.rejectedWith(JSON.stringify(json)); + }); +}); diff --git a/packages/data-connect/test/unit/queries.test.ts b/packages/data-connect/test/unit/queries.test.ts index c96ca6d5a0b..1e80764385b 100644 --- a/packages/data-connect/test/unit/queries.test.ts +++ b/packages/data-connect/test/unit/queries.test.ts @@ -54,7 +54,7 @@ describe('Queries', () => { const rt = new RESTTransport(options, undefined, authProvider); await expect( rt.invokeQuery('test', null) - ).to.eventually.be.rejectedWith(JSON.stringify(json)); + ).to.eventually.be.rejectedWith(json.message); expect(fakeFetchImpl.callCount).to.eq(2); }); it('[MUTATION] should retry auth whenever the fetcher returns with unauthorized', async () => { @@ -63,7 +63,7 @@ describe('Queries', () => { const rt = new RESTTransport(options, undefined, authProvider); await expect( rt.invokeMutation('test', null) - ).to.eventually.be.rejectedWith(JSON.stringify(json)); + ).to.eventually.be.rejectedWith(json.message); expect(fakeFetchImpl.callCount).to.eq(2); }); it("should not retry auth whenever the fetcher returns with unauthorized and the token doesn't change", async () => { @@ -73,7 +73,7 @@ describe('Queries', () => { rt._setLastToken('initial token'); await expect( rt.invokeQuery('test', null) as Promise - ).to.eventually.be.rejectedWith(JSON.stringify(json)); + ).to.eventually.be.rejectedWith(json.message); expect(fakeFetchImpl.callCount).to.eq(1); }); });