apollo-link-http
Advanced tools
Comparing version 1.4.0 to 1.5.0
@@ -5,2 +5,5 @@ # Change log | ||
### 1.5.0 | ||
- New useGETForQueries option: if set, uses GET for queries (but not mutations) | ||
### 1.4.0 | ||
@@ -7,0 +10,0 @@ - move logic to apollo-link-http-core [PR#364](https://github.com/apollographql/apollo-link/pull/364) |
@@ -28,3 +28,3 @@ (function (global, factory) { | ||
if (linkOptions === void 0) { linkOptions = {}; } | ||
var _a = linkOptions.uri, uri = _a === void 0 ? '/graphql' : _a, fetcher = linkOptions.fetch, includeExtensions = linkOptions.includeExtensions, requestOptions = __rest(linkOptions, ["uri", "fetch", "includeExtensions"]); | ||
var _a = linkOptions.uri, uri = _a === void 0 ? '/graphql' : _a, fetcher = linkOptions.fetch, includeExtensions = linkOptions.includeExtensions, useGETForQueries = linkOptions.useGETForQueries, requestOptions = __rest(linkOptions, ["uri", "fetch", "includeExtensions", "useGETForQueries"]); | ||
apolloLinkHttpCommon.checkFetcher(fetcher); | ||
@@ -53,42 +53,15 @@ if (!fetcher) { | ||
options.signal = signal; | ||
var definitionIsMutation = function (d) { | ||
return d.kind === 'OperationDefinition' && d.operation === 'mutation'; | ||
}; | ||
if (useGETForQueries && | ||
!operation.query.definitions.some(definitionIsMutation)) { | ||
options.method = 'GET'; | ||
} | ||
if (options.method === 'GET') { | ||
var queryParams_1 = []; | ||
var addQueryParam = function (key, value) { | ||
queryParams_1.push(key + "=" + encodeURIComponent(value)); | ||
}; | ||
if ('query' in body) { | ||
addQueryParam('query', body.query); | ||
var _c = rewriteURIForGET(chosenURI, body), newURI = _c.newURI, parseError = _c.parseError; | ||
if (parseError) { | ||
return apolloLink.fromError(parseError); | ||
} | ||
if (body.operationName) { | ||
addQueryParam('operationName', body.operationName); | ||
} | ||
if (body.variables) { | ||
var serializedVariables = void 0; | ||
try { | ||
serializedVariables = apolloLinkHttpCommon.serializeFetchParameter(body.variables, 'Variables map'); | ||
} | ||
catch (parseError) { | ||
return apolloLink.fromError(parseError); | ||
} | ||
addQueryParam('variables', serializedVariables); | ||
} | ||
if (body.extensions) { | ||
var serializedExtensions = void 0; | ||
try { | ||
serializedExtensions = apolloLinkHttpCommon.serializeFetchParameter(body.extensions, 'Extensions map'); | ||
} | ||
catch (parseError) { | ||
return apolloLink.fromError(parseError); | ||
} | ||
addQueryParam('extensions', serializedExtensions); | ||
} | ||
var fragment = '', preFragment = chosenURI; | ||
var fragmentStart = chosenURI.indexOf('#'); | ||
if (fragmentStart !== -1) { | ||
fragment = chosenURI.substr(fragmentStart); | ||
preFragment = chosenURI.substr(0, fragmentStart); | ||
} | ||
var queryParamsPrefix = preFragment.indexOf('?') === -1 ? '?' : '&'; | ||
chosenURI = | ||
preFragment + queryParamsPrefix + queryParams_1.join('&') + fragment; | ||
chosenURI = newURI; | ||
} | ||
@@ -130,2 +103,43 @@ else { | ||
}; | ||
function rewriteURIForGET(chosenURI, body) { | ||
var queryParams = []; | ||
var addQueryParam = function (key, value) { | ||
queryParams.push(key + "=" + encodeURIComponent(value)); | ||
}; | ||
if ('query' in body) { | ||
addQueryParam('query', body.query); | ||
} | ||
if (body.operationName) { | ||
addQueryParam('operationName', body.operationName); | ||
} | ||
if (body.variables) { | ||
var serializedVariables = void 0; | ||
try { | ||
serializedVariables = apolloLinkHttpCommon.serializeFetchParameter(body.variables, 'Variables map'); | ||
} | ||
catch (parseError) { | ||
return { parseError: parseError }; | ||
} | ||
addQueryParam('variables', serializedVariables); | ||
} | ||
if (body.extensions) { | ||
var serializedExtensions = void 0; | ||
try { | ||
serializedExtensions = apolloLinkHttpCommon.serializeFetchParameter(body.extensions, 'Extensions map'); | ||
} | ||
catch (parseError) { | ||
return { parseError: parseError }; | ||
} | ||
addQueryParam('extensions', serializedExtensions); | ||
} | ||
var fragment = '', preFragment = chosenURI; | ||
var fragmentStart = chosenURI.indexOf('#'); | ||
if (fragmentStart !== -1) { | ||
fragment = chosenURI.substr(fragmentStart); | ||
preFragment = chosenURI.substr(0, fragmentStart); | ||
} | ||
var queryParamsPrefix = preFragment.indexOf('?') === -1 ? '?' : '&'; | ||
var newURI = preFragment + queryParamsPrefix + queryParams.join('&') + fragment; | ||
return { newURI: newURI }; | ||
} | ||
var HttpLink = (function (_super) { | ||
@@ -132,0 +146,0 @@ __extends(HttpLink, _super); |
@@ -7,2 +7,3 @@ import { ApolloLink, RequestHandler } from 'apollo-link'; | ||
interface Options extends HttpOptions { | ||
useGETForQueries?: boolean; | ||
} | ||
@@ -9,0 +10,0 @@ } |
@@ -24,3 +24,3 @@ var __extends = (this && this.__extends) || (function () { | ||
if (linkOptions === void 0) { linkOptions = {}; } | ||
var _a = linkOptions.uri, uri = _a === void 0 ? '/graphql' : _a, fetcher = linkOptions.fetch, includeExtensions = linkOptions.includeExtensions, requestOptions = __rest(linkOptions, ["uri", "fetch", "includeExtensions"]); | ||
var _a = linkOptions.uri, uri = _a === void 0 ? '/graphql' : _a, fetcher = linkOptions.fetch, includeExtensions = linkOptions.includeExtensions, useGETForQueries = linkOptions.useGETForQueries, requestOptions = __rest(linkOptions, ["uri", "fetch", "includeExtensions", "useGETForQueries"]); | ||
checkFetcher(fetcher); | ||
@@ -49,42 +49,15 @@ if (!fetcher) { | ||
options.signal = signal; | ||
var definitionIsMutation = function (d) { | ||
return d.kind === 'OperationDefinition' && d.operation === 'mutation'; | ||
}; | ||
if (useGETForQueries && | ||
!operation.query.definitions.some(definitionIsMutation)) { | ||
options.method = 'GET'; | ||
} | ||
if (options.method === 'GET') { | ||
var queryParams_1 = []; | ||
var addQueryParam = function (key, value) { | ||
queryParams_1.push(key + "=" + encodeURIComponent(value)); | ||
}; | ||
if ('query' in body) { | ||
addQueryParam('query', body.query); | ||
var _c = rewriteURIForGET(chosenURI, body), newURI = _c.newURI, parseError = _c.parseError; | ||
if (parseError) { | ||
return fromError(parseError); | ||
} | ||
if (body.operationName) { | ||
addQueryParam('operationName', body.operationName); | ||
} | ||
if (body.variables) { | ||
var serializedVariables = void 0; | ||
try { | ||
serializedVariables = serializeFetchParameter(body.variables, 'Variables map'); | ||
} | ||
catch (parseError) { | ||
return fromError(parseError); | ||
} | ||
addQueryParam('variables', serializedVariables); | ||
} | ||
if (body.extensions) { | ||
var serializedExtensions = void 0; | ||
try { | ||
serializedExtensions = serializeFetchParameter(body.extensions, 'Extensions map'); | ||
} | ||
catch (parseError) { | ||
return fromError(parseError); | ||
} | ||
addQueryParam('extensions', serializedExtensions); | ||
} | ||
var fragment = '', preFragment = chosenURI; | ||
var fragmentStart = chosenURI.indexOf('#'); | ||
if (fragmentStart !== -1) { | ||
fragment = chosenURI.substr(fragmentStart); | ||
preFragment = chosenURI.substr(0, fragmentStart); | ||
} | ||
var queryParamsPrefix = preFragment.indexOf('?') === -1 ? '?' : '&'; | ||
chosenURI = | ||
preFragment + queryParamsPrefix + queryParams_1.join('&') + fragment; | ||
chosenURI = newURI; | ||
} | ||
@@ -126,2 +99,43 @@ else { | ||
}; | ||
function rewriteURIForGET(chosenURI, body) { | ||
var queryParams = []; | ||
var addQueryParam = function (key, value) { | ||
queryParams.push(key + "=" + encodeURIComponent(value)); | ||
}; | ||
if ('query' in body) { | ||
addQueryParam('query', body.query); | ||
} | ||
if (body.operationName) { | ||
addQueryParam('operationName', body.operationName); | ||
} | ||
if (body.variables) { | ||
var serializedVariables = void 0; | ||
try { | ||
serializedVariables = serializeFetchParameter(body.variables, 'Variables map'); | ||
} | ||
catch (parseError) { | ||
return { parseError: parseError }; | ||
} | ||
addQueryParam('variables', serializedVariables); | ||
} | ||
if (body.extensions) { | ||
var serializedExtensions = void 0; | ||
try { | ||
serializedExtensions = serializeFetchParameter(body.extensions, 'Extensions map'); | ||
} | ||
catch (parseError) { | ||
return { parseError: parseError }; | ||
} | ||
addQueryParam('extensions', serializedExtensions); | ||
} | ||
var fragment = '', preFragment = chosenURI; | ||
var fragmentStart = chosenURI.indexOf('#'); | ||
if (fragmentStart !== -1) { | ||
fragment = chosenURI.substr(fragmentStart); | ||
preFragment = chosenURI.substr(0, fragmentStart); | ||
} | ||
var queryParamsPrefix = preFragment.indexOf('?') === -1 ? '?' : '&'; | ||
var newURI = preFragment + queryParamsPrefix + queryParams.join('&') + fragment; | ||
return { newURI: newURI }; | ||
} | ||
var HttpLink = (function (_super) { | ||
@@ -128,0 +142,0 @@ __extends(HttpLink, _super); |
{ | ||
"name": "apollo-link-http", | ||
"version": "1.4.0", | ||
"version": "1.5.0", | ||
"description": "HTTP transport layer for GraphQL", | ||
@@ -27,3 +27,4 @@ "author": "Evans Hauser <evanshauser@gmail.com>", | ||
"scripts": { | ||
"build:browser": "browserify ./lib/bundle.umd.js -o=./lib/bundle.js --i apollo-link && npm run minify:browser", | ||
"build:browser": | ||
"browserify ./lib/bundle.umd.js -o=./lib/bundle.js --i apollo-link && npm run minify:browser", | ||
"build": "tsc -p .", | ||
@@ -34,4 +35,6 @@ "bundle": "rollup -c", | ||
"filesize": "npm run build && npm run build:browser", | ||
"lint": "tslint --type-check -p tsconfig.json -c ../../tslint.json src/*.ts", | ||
"minify:browser": "uglifyjs -c -m -o ./lib/bundle.min.js -- ./lib/bundle.js", | ||
"lint": | ||
"tslint --type-check -p tsconfig.json -c ../../tslint.json src/*.ts", | ||
"minify:browser": | ||
"uglifyjs -c -m -o ./lib/bundle.min.js -- ./lib/bundle.js", | ||
"postbuild": "npm run bundle", | ||
@@ -72,14 +75,6 @@ "prebuild": "npm run clean", | ||
"testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$", | ||
"moduleFileExtensions": [ | ||
"ts", | ||
"tsx", | ||
"js", | ||
"json" | ||
], | ||
"moduleFileExtensions": ["ts", "tsx", "js", "json"], | ||
"mapCoverage": true, | ||
"testPathIgnorePatterns": [ | ||
"/node_modules/", | ||
"sharedHttpTests.ts" | ||
] | ||
"testPathIgnorePatterns": ["/node_modules/", "sharedHttpTests.ts"] | ||
} | ||
} |
@@ -38,2 +38,3 @@ --- | ||
* `fetchOptions`: any overrides of the fetch options argument to pass to the fetch call | ||
* `useGETForQueries`: set to `true` to use the HTTP `GET` method for queries (but not for mutations) | ||
@@ -48,3 +49,3 @@ <h2 id="fetch">Fetch polyfill</h2> | ||
Note that if you set `fetchOptions.method` to `GET`, the http link will follow the [standard GraphQL HTTP GET encoding](http://graphql.org/learn/serving-over-http/#get-request): the query, variables, operation name, and extensions will be passed as query parameters rather than in the HTTP request body. | ||
Note that if you set `fetchOptions.method` to `GET`, the http link will follow the [standard GraphQL HTTP GET encoding](http://graphql.org/learn/serving-over-http/#get-request): the query, variables, operation name, and extensions will be passed as query parameters rather than in the HTTP request body. If you want mutations to continue to be sent as non-idempotent `POST` requests, set the top-level `useGETForQueries` option to `true` instead of setting `fetchOptions.method` to `GET`. | ||
@@ -51,0 +52,0 @@ This link also attaches the response from the `fetch` operation on the context as `response` so you can access it from within another link. |
@@ -17,2 +17,10 @@ import { Observable, ApolloLink, execute } from 'apollo-link'; | ||
const sampleMutation = gql` | ||
mutation SampleMutation { | ||
stub { | ||
id | ||
} | ||
} | ||
`; | ||
const makeCallback = (done, body) => { | ||
@@ -135,5 +143,104 @@ return (...args) => { | ||
}); | ||
it('uses GET with useGETForQueries', done => { | ||
const variables = { params: 'stub' }; | ||
const link = createHttpLink({ | ||
uri: 'http://data/', | ||
useGETForQueries: true, | ||
}); | ||
execute(link, { | ||
query: sampleQuery, | ||
variables, | ||
}).subscribe( | ||
makeCallback(done, result => { | ||
const [uri, options] = fetchMock.lastCall(); | ||
const { method, body } = options; | ||
expect(body).toBeUndefined(); | ||
expect(method).toBe('GET'); | ||
expect(uri).toBe( | ||
'http://data/?query=query%20SampleQuery%20%7B%0A%20%20stub%20%7B%0A%20%20%20%20id%0A%20%20%7D%0A%7D%0A&operationName=SampleQuery&variables=%7B%22params%22%3A%22stub%22%7D', | ||
); | ||
}), | ||
); | ||
}); | ||
it('uses POST for mutations with useGETForQueries', done => { | ||
const variables = { params: 'stub' }; | ||
const link = createHttpLink({ | ||
uri: 'http://data/', | ||
useGETForQueries: true, | ||
}); | ||
execute(link, { | ||
query: sampleMutation, | ||
variables, | ||
}).subscribe( | ||
makeCallback(done, result => { | ||
const [uri, options] = fetchMock.lastCall(); | ||
const { method, body } = options; | ||
expect(body).toBeDefined(); | ||
expect(method).toBe('POST'); | ||
expect(uri).toBe('http://data/'); | ||
}), | ||
); | ||
}); | ||
}); | ||
it("throws for GET if the variables can't be stringified", done => { | ||
const link = createHttpLink({ | ||
uri: 'http://data/', | ||
useGETForQueries: true, | ||
}); | ||
let b; | ||
const a = { b }; | ||
b = { a }; | ||
a.b = b; | ||
const variables = { | ||
a, | ||
b, | ||
}; | ||
execute(link, { query: sampleQuery, variables }).subscribe( | ||
result => { | ||
done.fail('next should have been thrown from the link'); | ||
}, | ||
makeCallback(done, e => { | ||
expect(e.message).toMatch(/Variables map is not serializable/); | ||
expect(e.parseError.message).toMatch( | ||
/Converting circular structure to JSON/, | ||
); | ||
}), | ||
); | ||
}); | ||
it("throws for GET if the extensions can't be stringified", done => { | ||
const link = createHttpLink({ | ||
uri: 'http://data/', | ||
useGETForQueries: true, | ||
includeExtensions: true, | ||
}); | ||
let b; | ||
const a = { b }; | ||
b = { a }; | ||
a.b = b; | ||
const extensions = { | ||
a, | ||
b, | ||
}; | ||
execute(link, { query: sampleQuery, extensions }).subscribe( | ||
result => { | ||
done.fail('next should have been thrown from the link'); | ||
}, | ||
makeCallback(done, e => { | ||
expect(e.message).toMatch(/Extensions map is not serializable/); | ||
expect(e.parseError.message).toMatch( | ||
/Converting circular structure to JSON/, | ||
); | ||
}), | ||
); | ||
}); | ||
sharedHttpTest('HttpLink', createHttpLink); | ||
}); |
@@ -10,5 +10,7 @@ import { ApolloLink, Observable, RequestHandler, fromError } from 'apollo-link'; | ||
fallbackHttpConfig, | ||
Body, | ||
HttpOptions, | ||
UriFunction as _UriFunction, | ||
} from 'apollo-link-http-common'; | ||
import { DefinitionNode } from 'graphql'; | ||
@@ -18,3 +20,10 @@ export namespace HttpLink { | ||
export interface UriFunction extends _UriFunction {} | ||
export interface Options extends HttpOptions {} | ||
export interface Options extends HttpOptions { | ||
/** | ||
* If set to true, use the HTTP GET method for query operations. Mutations | ||
* will still use the method specified in fetchOptions.method (which defaults | ||
* to POST). | ||
*/ | ||
useGETForQueries?: boolean; | ||
} | ||
} | ||
@@ -32,2 +41,3 @@ | ||
includeExtensions, | ||
useGETForQueries, | ||
...requestOptions | ||
@@ -76,57 +86,19 @@ } = linkOptions; | ||
// If requested, set method to GET if there are no mutations. | ||
const definitionIsMutation = (d: DefinitionNode) => { | ||
return d.kind === 'OperationDefinition' && d.operation === 'mutation'; | ||
}; | ||
if ( | ||
useGETForQueries && | ||
!operation.query.definitions.some(definitionIsMutation) | ||
) { | ||
options.method = 'GET'; | ||
} | ||
if (options.method === 'GET') { | ||
// Implement the standard HTTP GET serialization, plus 'extensions'. Note | ||
// the extra level of JSON serialization! | ||
const queryParams = []; | ||
const addQueryParam = (key: string, value: string) => { | ||
queryParams.push(`${key}=${encodeURIComponent(value)}`); | ||
}; | ||
if ('query' in body) { | ||
addQueryParam('query', body.query); | ||
const { newURI, parseError } = rewriteURIForGET(chosenURI, body); | ||
if (parseError) { | ||
return fromError(parseError); | ||
} | ||
if (body.operationName) { | ||
addQueryParam('operationName', body.operationName); | ||
} | ||
if (body.variables) { | ||
let serializedVariables; | ||
try { | ||
serializedVariables = serializeFetchParameter( | ||
body.variables, | ||
'Variables map', | ||
); | ||
} catch (parseError) { | ||
return fromError(parseError); | ||
} | ||
addQueryParam('variables', serializedVariables); | ||
} | ||
if (body.extensions) { | ||
let serializedExtensions; | ||
try { | ||
serializedExtensions = serializeFetchParameter( | ||
body.extensions, | ||
'Extensions map', | ||
); | ||
} catch (parseError) { | ||
return fromError(parseError); | ||
} | ||
addQueryParam('extensions', serializedExtensions); | ||
} | ||
// Reconstruct the URI with added query params. | ||
// XXX This assumes that the URI is well-formed and that it doesn't | ||
// already contain any of these query params. We could instead use the | ||
// URL API and take a polyfill (whatwg-url@6) for older browsers that | ||
// don't support URLSearchParams. Note that some browsers (and | ||
// versions of whatwg-url) support URL but not URLSearchParams! | ||
let fragment = '', | ||
preFragment = chosenURI; | ||
const fragmentStart = chosenURI.indexOf('#'); | ||
if (fragmentStart !== -1) { | ||
fragment = chosenURI.substr(fragmentStart); | ||
preFragment = chosenURI.substr(0, fragmentStart); | ||
} | ||
const queryParamsPrefix = preFragment.indexOf('?') === -1 ? '?' : '&'; | ||
chosenURI = | ||
preFragment + queryParamsPrefix + queryParams.join('&') + fragment; | ||
chosenURI = newURI; | ||
} else { | ||
@@ -201,2 +173,62 @@ try { | ||
// For GET operations, returns the given URI rewritten with parameters, or a | ||
// parse error. | ||
function rewriteURIForGET(chosenURI: string, body: Body) { | ||
// Implement the standard HTTP GET serialization, plus 'extensions'. Note | ||
// the extra level of JSON serialization! | ||
const queryParams = []; | ||
const addQueryParam = (key: string, value: string) => { | ||
queryParams.push(`${key}=${encodeURIComponent(value)}`); | ||
}; | ||
if ('query' in body) { | ||
addQueryParam('query', body.query); | ||
} | ||
if (body.operationName) { | ||
addQueryParam('operationName', body.operationName); | ||
} | ||
if (body.variables) { | ||
let serializedVariables; | ||
try { | ||
serializedVariables = serializeFetchParameter( | ||
body.variables, | ||
'Variables map', | ||
); | ||
} catch (parseError) { | ||
return { parseError }; | ||
} | ||
addQueryParam('variables', serializedVariables); | ||
} | ||
if (body.extensions) { | ||
let serializedExtensions; | ||
try { | ||
serializedExtensions = serializeFetchParameter( | ||
body.extensions, | ||
'Extensions map', | ||
); | ||
} catch (parseError) { | ||
return { parseError }; | ||
} | ||
addQueryParam('extensions', serializedExtensions); | ||
} | ||
// Reconstruct the URI with added query params. | ||
// XXX This assumes that the URI is well-formed and that it doesn't | ||
// already contain any of these query params. We could instead use the | ||
// URL API and take a polyfill (whatwg-url@6) for older browsers that | ||
// don't support URLSearchParams. Note that some browsers (and | ||
// versions of whatwg-url) support URL but not URLSearchParams! | ||
let fragment = '', | ||
preFragment = chosenURI; | ||
const fragmentStart = chosenURI.indexOf('#'); | ||
if (fragmentStart !== -1) { | ||
fragment = chosenURI.substr(fragmentStart); | ||
preFragment = chosenURI.substr(0, fragmentStart); | ||
} | ||
const queryParamsPrefix = preFragment.indexOf('?') === -1 ? '?' : '&'; | ||
const newURI = | ||
preFragment + queryParamsPrefix + queryParams.join('&') + fragment; | ||
return { newURI }; | ||
} | ||
export class HttpLink extends ApolloLink { | ||
@@ -203,0 +235,0 @@ public requester: RequestHandler; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
83680
1453
317
2