Skip to content

Commit

Permalink
Ensure Apollo uses core.http.fetch
Browse files Browse the repository at this point in the history
  • Loading branch information
Kerry350 committed Feb 17, 2020
1 parent 87fa820 commit e967ce0
Showing 1 changed file with 52 additions and 8 deletions.
60 changes: 52 additions & 8 deletions x-pack/plugins/infra/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
import { InMemoryCache, IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
import ApolloClient from 'apollo-client';
import { ApolloLink } from 'apollo-link';
import { HttpLink } from 'apollo-link-http';
import { createHttpLink } from 'apollo-link-http';
import { withClientState } from 'apollo-link-state';
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils';
import { InfraFrontendLibs } from './lib/lib';
Expand All @@ -28,6 +28,7 @@ import { DataPublicPluginSetup, DataPublicPluginStart } from '../../../../src/pl
import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/public';
import { DataEnhancedSetup, DataEnhancedStart } from '../../data_enhanced/public';
import { LogsRouter, MetricsRouter } from './routers';
import { HttpFetchOptions } from 'src/core/public';

export type ClientSetup = void;
export type ClientStart = void;
Expand Down Expand Up @@ -134,20 +135,63 @@ export class Plugin
basePath: core.http.basePath.get(),
});

const wrappedFetch = (path: string, options: HttpFetchOptions) => {
return new Promise<Response>(async (resolve, reject) => {
// core.http.fetch isn't 100% compatible with the Fetch API and will
// throw Errors on 401s. This top level try / catch handles those scenarios.
try {
core.http.fetch(
path,
{
...options,
asResponse: true,
}
).then((res) => {
if (!res.response) {
return reject();
}
// core.http.fetch will parse the Response and set a body before handing it back. As such .text() / .json()
// will have already been called on the Response instance. However, Apollo will also want to call
// .text() / .json() on the instance, as it expects the raw Response instance, rather than core's wrapper.
// .text() / .json() can only be called once, and an Error will be thrown if those methods are accessed again.
// This hacks around that by setting up a new .text() method that will restringify the JSON response we already have.
// This does result in an extra stringify / parse cycle, which isn't ideal, but as we only have a few endpoints left using
// GraphQL this shouldn't create excessive overhead.
// Ref: https://github.com/apollographql/apollo-link/blob/master/packages/apollo-link-http/src/httpLink.ts#L134
// and
// https://github.com/apollographql/apollo-link/blob/master/packages/apollo-link-http-common/src/index.ts#L125
return resolve({
...res.response,
text: () => {
return new Promise(async (resolveText, rejectText) => {
if (res.body) {
return resolveText(JSON.stringify(res.body));
} else {
return rejectText();
}
})
}
});
});
} catch(error) {
reject(error);
}
});
};

const HttpLink = createHttpLink({
fetch: wrappedFetch,
uri: `/api/infra/graphql`,
});

const graphQLOptions = {
cache,
link: ApolloLink.from([
withClientState({
cache,
resolvers: {},
}),
new HttpLink({
credentials: 'same-origin',
headers: {
'kbn-xsrf': true,
},
uri: `${core.http.basePath.get()}/api/infra/graphql`,
}),
HttpLink,
]),
};

Expand Down

0 comments on commit e967ce0

Please sign in to comment.