Socket
Socket
Sign inDemoInstall

apollo-link-state

Package Overview
Dependencies
Maintainers
4
Versions
13
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

apollo-link-state - npm Package Compare versions

Comparing version 0.4.1 to 0.4.2

.vscode/settings.json

9

CHANGELOG.md
# Change log
### vNext
### vNEXT
### 0.4.2
- Allow providing resolvers via function [#293](https://github.com/apollographql/apollo-link-state/pull/293)
- Add support for DocumentNode input to `typeDefs` [#284](https://github.com/apollographql/apollo-link-state/pull/284)
- Remove dependency on zen-observable's flatMap [#284](https://github.com/apollographql/apollo-link-state/pull/284)
### 0.4.1
- Return defaults when no Queries resolver is found and check for `resolverMap` [#202](https://github.com/apollographql/apollo-link-state/pull/202)

@@ -5,0 +12,0 @@ - Fix for merging local & remote data not part of same selection set [#193](https://github.com/apollographql/apollo-link-state/pull/193)

80

lib/bundle.umd.js
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('apollo-utilities'), require('apollo-link'), require('graphql-anywhere/lib/async')) :
typeof define === 'function' && define.amd ? define(['exports', 'apollo-utilities', 'apollo-link', 'graphql-anywhere/lib/async'], factory) :
(factory((global.apolloLink = global.apolloLink || {}, global.apolloLink.state = {}),global.apollo.utilities,global.apolloLink.core,global.graphqlAnywhere.async));
}(this, (function (exports,apolloUtilities,apolloLink,async) { 'use strict';
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('graphql'), require('apollo-utilities'), require('apollo-link'), require('graphql-anywhere/lib/async')) :
typeof define === 'function' && define.amd ? define(['exports', 'graphql', 'apollo-utilities', 'apollo-link', 'graphql-anywhere/lib/async'], factory) :
(factory((global.apolloLink = global.apolloLink || {}, global.apolloLink.state = {}),global.graphql,global.apollo.utilities,global.apolloLink.core,global.graphqlAnywhere.async));
}(this, (function (exports,graphql,apolloUtilities,apolloLink,Async) { 'use strict';

@@ -21,2 +21,9 @@ var connectionRemoveConfig = {

}
function normalizeTypeDefs(typeDefs) {
var defs = Array.isArray(typeDefs) ? typeDefs : [typeDefs];
return defs
.map(function (typeDef) { return (typeof typeDef === 'string' ? typeDef : graphql.print(typeDef)); })
.map(function (str) { return str.trim(); })
.join('\n');
}

@@ -33,6 +40,7 @@ var __extends = (undefined && undefined.__extends) || (function () {

})();
var graphql$1 = Async.graphql;
var capitalizeFirstLetter = function (str) { return str.charAt(0).toUpperCase() + str.slice(1); };
var withClientState = function (clientStateConfig) {
if (clientStateConfig === void 0) { clientStateConfig = { resolvers: {}, defaults: {} }; }
var resolvers = clientStateConfig.resolvers, defaults = clientStateConfig.defaults, cache = clientStateConfig.cache, typeDefs = clientStateConfig.typeDefs;
var defaults = clientStateConfig.defaults, cache = clientStateConfig.cache, typeDefs = clientStateConfig.typeDefs, fragmentMatcher = clientStateConfig.fragmentMatcher;
if (cache && defaults) {

@@ -55,5 +63,3 @@ cache.writeData({ data: defaults });

var directives_1 = 'directive @client on FIELD';
var definition_1 = typeof typeDefs === 'string'
? typeDefs
: typeDefs.map(function (typeDef) { return typeDef.trim(); }).join('\n');
var definition_1 = normalizeTypeDefs(typeDefs);
operation.setContext(function (_a) {

@@ -69,2 +75,5 @@ var _b = _a.schemas, schemas = _b === void 0 ? [] : _b;

return forward(operation);
var resolvers = typeof clientStateConfig.resolvers === 'function'
? clientStateConfig.resolvers()
: clientStateConfig.resolvers;
var server = removeClientSetsFromDocument(operation.query);

@@ -75,5 +84,9 @@ var query = operation.query;

if (rootValue === void 0) { rootValue = {}; }
var fieldValue = rootValue[info.resultKey];
if (fieldValue !== undefined)
return fieldValue;
var resultKey = info.resultKey;
var aliasedNode = rootValue[resultKey];
var preAliasingNode = rootValue[fieldName];
var aliasNeeded = resultKey !== fieldName;
if (aliasedNode !== undefined || preAliasingNode !== undefined) {
return aliasedNode || preAliasingNode;
}
var resolverMap = resolvers[rootValue.__typename || type];

@@ -85,18 +98,24 @@ if (resolverMap) {

}
return defaults[fieldName];
return ((aliasNeeded ? aliasedNode : preAliasingNode) ||
(defaults || {})[fieldName]);
};
if (server)
operation.query = server;
var obs = server && forward
? forward(operation)
: apolloLink.Observable.of({
data: {},
});
return new apolloLink.Observable(function (observer) {
if (server)
operation.query = server;
var obs = server && forward
? forward(operation)
: apolloLink.Observable.of({
data: {},
});
var observerErrorHandler = observer.error.bind(observer);
var sub = obs.subscribe({
var complete = false;
var handlingNext = false;
obs.subscribe({
next: function (_a) {
var data = _a.data, errors = _a.errors;
var observerErrorHandler = observer.error.bind(observer);
var context = operation.getContext();
async.graphql(resolver, query, data, context, operation.variables)
handlingNext = true;
graphql$1(resolver, query, data, context, operation.variables, {
fragmentMatcher: fragmentMatcher,
})
.then(function (nextData) {

@@ -107,12 +126,17 @@ observer.next({

});
observer.complete();
if (complete) {
observer.complete();
}
handlingNext = false;
})
.catch(observerErrorHandler);
},
error: observerErrorHandler,
error: observer.error.bind(observer),
complete: function () {
if (!handlingNext) {
observer.complete();
}
complete = true;
},
});
return function () {
if (sub)
sub.unsubscribe();
};
});

@@ -119,0 +143,0 @@ };

/// <reference types="zen-observable" />
import { ApolloLink, Observable, Operation, NextLink, FetchResult } from 'apollo-link';
import { ApolloCache } from 'apollo-cache';
import { DocumentNode } from 'graphql';
import { FragmentMatcher } from 'graphql-anywhere';
export declare type ClientStateConfig = {
cache?: ApolloCache<any>;
resolvers: any;
resolvers: any | (() => any);
defaults?: any;
typeDefs?: string | string[];
typeDefs?: string | string[] | DocumentNode | DocumentNode[];
fragmentMatcher?: FragmentMatcher;
};

@@ -10,0 +13,0 @@ export declare const withClientState: (clientStateConfig?: ClientStateConfig) => {

@@ -13,8 +13,9 @@ var __extends = (this && this.__extends) || (function () {

import { hasDirectives, getMainDefinition } from 'apollo-utilities';
import { graphql } from 'graphql-anywhere/lib/async';
import { removeClientSetsFromDocument } from './utils';
import * as Async from 'graphql-anywhere/lib/async';
var graphql = Async.graphql;
import { removeClientSetsFromDocument, normalizeTypeDefs } from './utils';
var capitalizeFirstLetter = function (str) { return str.charAt(0).toUpperCase() + str.slice(1); };
export var withClientState = function (clientStateConfig) {
if (clientStateConfig === void 0) { clientStateConfig = { resolvers: {}, defaults: {} }; }
var resolvers = clientStateConfig.resolvers, defaults = clientStateConfig.defaults, cache = clientStateConfig.cache, typeDefs = clientStateConfig.typeDefs;
var defaults = clientStateConfig.defaults, cache = clientStateConfig.cache, typeDefs = clientStateConfig.typeDefs, fragmentMatcher = clientStateConfig.fragmentMatcher;
if (cache && defaults) {

@@ -37,5 +38,3 @@ cache.writeData({ data: defaults });

var directives_1 = 'directive @client on FIELD';
var definition_1 = typeof typeDefs === 'string'
? typeDefs
: typeDefs.map(function (typeDef) { return typeDef.trim(); }).join('\n');
var definition_1 = normalizeTypeDefs(typeDefs);
operation.setContext(function (_a) {

@@ -51,2 +50,5 @@ var _b = _a.schemas, schemas = _b === void 0 ? [] : _b;

return forward(operation);
var resolvers = typeof clientStateConfig.resolvers === 'function'
? clientStateConfig.resolvers()
: clientStateConfig.resolvers;
var server = removeClientSetsFromDocument(operation.query);

@@ -57,5 +59,9 @@ var query = operation.query;

if (rootValue === void 0) { rootValue = {}; }
var fieldValue = rootValue[info.resultKey];
if (fieldValue !== undefined)
return fieldValue;
var resultKey = info.resultKey;
var aliasedNode = rootValue[resultKey];
var preAliasingNode = rootValue[fieldName];
var aliasNeeded = resultKey !== fieldName;
if (aliasedNode !== undefined || preAliasingNode !== undefined) {
return aliasedNode || preAliasingNode;
}
var resolverMap = resolvers[rootValue.__typename || type];

@@ -67,18 +73,24 @@ if (resolverMap) {

}
return defaults[fieldName];
return ((aliasNeeded ? aliasedNode : preAliasingNode) ||
(defaults || {})[fieldName]);
};
if (server)
operation.query = server;
var obs = server && forward
? forward(operation)
: Observable.of({
data: {},
});
return new Observable(function (observer) {
if (server)
operation.query = server;
var obs = server && forward
? forward(operation)
: Observable.of({
data: {},
});
var observerErrorHandler = observer.error.bind(observer);
var sub = obs.subscribe({
var complete = false;
var handlingNext = false;
obs.subscribe({
next: function (_a) {
var data = _a.data, errors = _a.errors;
var observerErrorHandler = observer.error.bind(observer);
var context = operation.getContext();
graphql(resolver, query, data, context, operation.variables)
handlingNext = true;
graphql(resolver, query, data, context, operation.variables, {
fragmentMatcher: fragmentMatcher,
})
.then(function (nextData) {

@@ -89,12 +101,17 @@ observer.next({

});
observer.complete();
if (complete) {
observer.complete();
}
handlingNext = false;
})
.catch(observerErrorHandler);
},
error: observerErrorHandler,
error: observer.error.bind(observer),
complete: function () {
if (!handlingNext) {
observer.complete();
}
complete = true;
},
});
return function () {
if (sub)
sub.unsubscribe();
};
});

@@ -101,0 +118,0 @@ };

import { DocumentNode } from 'graphql';
export declare function removeClientSetsFromDocument(query: DocumentNode): DocumentNode;
export declare function normalizeTypeDefs(typeDefs: string | string[] | DocumentNode | DocumentNode[]): string;

@@ -1,2 +0,3 @@

import { checkDocument, removeDirectivesFromDocument, } from 'apollo-utilities';
import { print } from 'graphql';
import { checkDocument, removeDirectivesFromDocument } from 'apollo-utilities';
var connectionRemoveConfig = {

@@ -16,2 +17,9 @@ test: function (directive) { return directive.name.value === 'client'; },

}
export function normalizeTypeDefs(typeDefs) {
var defs = Array.isArray(typeDefs) ? typeDefs : [typeDefs];
return defs
.map(function (typeDef) { return (typeof typeDef === 'string' ? typeDef : print(typeDef)); })
.map(function (str) { return str.trim(); })
.join('\n');
}
//# sourceMappingURL=utils.js.map
{
"name": "apollo-link-state",
"version": "0.4.1",
"version": "0.4.2",
"description": "An easy way to manage local state with Apollo Link",

@@ -20,4 +20,3 @@ "author": "James Baxley <james@meteor.com>",

"scripts": {
"build:browser":
"browserify ./lib/bundle.umd.js -o=./lib/bundle.js --i apollo-link --i apollo-utilities --i graphql-anywhere && npm run minify:browser",
"build:browser": "browserify ./lib/bundle.umd.js -o=./lib/bundle.js --i apollo-link --i apollo-utilities --i graphql-anywhere -i graphql && npm run minify:browser",
"build": "tsc -p .",

@@ -28,8 +27,6 @@ "bundle": "rollup -c",

"prelint": "npm run lint-fix",
"lint-fix":
"prettier --trailing-comma all --single-quote --write \"src/**/*.{j,t}s*\"",
"lint-fix": "prettier --trailing-comma all --single-quote --write \"src/**/*.{j,t}s*\"",
"lint": "tslint --type-check -p tsconfig.json -c tslint.json src/*.ts",
"lint-staged": "lint-staged",
"minify:browser":
"uglifyjs -c -m -o ./lib/bundle.min.js -- ./lib/bundle.js",
"minify:browser": "uglifyjs -c -m -o ./lib/bundle.min.js -- ./lib/bundle.js",
"postbuild": "npm run bundle",

@@ -53,3 +50,3 @@ "prebuild": "npm run clean",

"devDependencies": {
"@types/graphql": "0.11.5",
"@types/graphql": "0.12.7",
"@types/jest": "22.1.x",

@@ -63,3 +60,3 @@ "apollo-cache-inmemory": "^1.1.5",

"danger": "1.2.0",
"graphql": "0.11.7",
"graphql": "0.12.3",
"graphql-tag": "2.5.0",

@@ -85,3 +82,8 @@ "jest": "21.2.1",

"testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$",
"moduleFileExtensions": ["ts", "tsx", "js", "json"]
"moduleFileExtensions": [
"ts",
"tsx",
"js",
"json"
]
},

@@ -93,13 +95,12 @@ "dependencies": {

"lint-staged": {
"*.ts*": [
"*.{ts, tsx, js, jsx}": [
"prettier --trailing-comma all --single-quote --write",
"git add"
],
"*.js*": [
"prettier --trailing-comma all --single-quote --write",
"!(package).json": [
"prettier --write",
"git add"
],
"*.json*": ["prettier --write", "git add"]
]
},
"pre-commit": "lint-staged"
}

@@ -208,3 +208,3 @@ import gql from 'graphql-tag';

next: ({ data }) => {
expect({ ...data }).toEqual({ count: 0, lastCount: 1 });
expect({ ...data }).toMatchObject({ count: 0, lastCount: 1 });
done();

@@ -251,3 +251,3 @@ },

try {
expect({ ...data.user }).toEqual({
expect({ ...data.user }).toMatchObject({
firstName: 'John',

@@ -337,3 +337,3 @@ lastName: 'Doe',

expect(data.count).toEqual(0);
expect({ ...data.user }).toEqual({
expect({ ...data.user }).toMatchObject({
__typename: 'User',

@@ -356,3 +356,3 @@ firstName: 'John',

expect(data.count).toEqual(1);
expect({ ...data.user }).toEqual({
expect({ ...data.user }).toMatchObject({
__typename: 'User',

@@ -359,0 +359,0 @@ firstName: 'Harry',

import gql from 'graphql-tag';
import { ApolloLink, execute, Observable } from 'apollo-link';
import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import {
InMemoryCache,
IntrospectionFragmentMatcher,
} from 'apollo-cache-inmemory';

@@ -40,3 +43,3 @@ import { print } from 'graphql/language/printer';

return client.query({ query }).then(({ data }) => {
expect({ ...data }).toEqual({ field: 1 });
expect({ ...data }).toMatchObject({ field: 1 });
});

@@ -85,3 +88,3 @@ });

return client.query({ query }).then(({ data }) => {
expect({ ...data }).toEqual({ field: 1 });
expect({ ...data }).toMatchObject({ field: 1 });
});

@@ -116,3 +119,3 @@ });

.then(({ data }) => {
expect({ ...data }).toEqual({ field: 1 });
expect({ ...data }).toMatchObject({ field: 1 });
expect(count).toBe(1);

@@ -122,3 +125,3 @@ })

client.query({ query }).then(({ data }) => {
expect({ ...data }).toEqual({ field: 1 });
expect({ ...data }).toMatchObject({ field: 1 });
expect(count).toBe(1);

@@ -155,3 +158,3 @@ }),

.then(({ data }) => {
expect({ ...data }).toEqual({ field: 1 });
expect({ ...data }).toMatchObject({ field: 1 });
expect(count).toBe(1);

@@ -163,3 +166,3 @@ })

.then(({ data }) => {
expect({ ...data }).toEqual({ field: 1 });
expect({ ...data }).toMatchObject({ field: 1 });
expect(count).toBe(2);

@@ -169,2 +172,83 @@ }),

});
it('supports subscriptions', done => {
const query = gql`
subscription {
field
}
`;
const link = new ApolloLink(() =>
Observable.of({ data: { field: 1 } }, { data: { field: 2 } }),
);
const local = withClientState();
const client = new ApolloClient({
cache: new InMemoryCache(),
link: local.concat(link),
});
let counter = 0;
expect.assertions(2);
return client.subscribe({ query }).forEach(item => {
expect(item).toMatchObject({ data: { field: ++counter } });
if (counter === 2) {
done();
}
});
});
it('uses fragment matcher', () => {
const query = gql`
{
foo {
... on Bar {
bar @client
}
... on Baz {
baz @client
}
}
}
`;
const link = new ApolloLink(() =>
Observable.of({
data: { foo: [{ __typename: 'Bar' }, { __typename: 'Baz' }] },
}),
);
const local = withClientState({
resolvers: {
Bar: {
bar: () => 'Bar',
},
Baz: {
baz: () => 'Baz',
},
},
fragmentMatcher: ({ __typename }, typeCondition) =>
__typename === typeCondition,
});
const client = new ApolloClient({
cache: new InMemoryCache({
fragmentMatcher: new IntrospectionFragmentMatcher({
introspectionQueryResultData: {
__schema: {
types: [
{
kind: 'UnionTypeDefinition',
name: 'Foo',
possibleTypes: [{ name: 'Bar' }, { name: 'Baz' }],
},
],
},
},
}),
}),
link: local.concat(link),
});
return client.query({ query }).then(({ data }) => {
expect(data).toMatchObject({ foo: [{ bar: 'Bar' }, { baz: 'Baz' }] });
});
});
});

@@ -190,3 +274,3 @@

.query({ query })
.then(({ data }) => expect({ ...data }).toEqual({ field: 'yo' }));
.then(({ data }) => expect({ ...data }).toMatchObject({ field: 'yo' }));
});

@@ -226,3 +310,3 @@ it('lets you write to the cache with a mutation', () => {

.then(({ data }) => {
expect({ ...data }).toEqual({ field: 1 });
expect({ ...data }).toMatchObject({ field: 1 });
});

@@ -267,3 +351,3 @@ });

if (count === 1) {
expect({ ...data }).toEqual({ field: 0 });
expect({ ...data }).toMatchObject({ field: 0 });
client.mutate({ mutation });

@@ -273,3 +357,3 @@ }

if (count === 2) {
expect({ ...data }).toEqual({ field: 1 });
expect({ ...data }).toMatchObject({ field: 1 });
done();

@@ -323,38 +407,6 @@ }

.then(({ data }) => {
expect({ ...data }).toEqual({ field: '1234' });
expect({ ...data }).toMatchObject({ field: '1234' });
});
});
it('runs default resolvers for aliased fields tagged with @client', () => {
const query = gql`
{
fie: foo @client {
bar
}
}
`;
const cache = new InMemoryCache();
const client = new ApolloClient({
cache,
link: withClientState({
cache,
resolvers: {},
defaults: {
foo: {
bar: 'yo',
__typename: 'Foo',
},
},
}),
});
return client.query({ query }).then(({ data }) => {
expect({ ...data }).toMatchObject({
fie: { bar: 'yo', __typename: 'Foo' },
});
});
});
it('writeDefaults lets you write defaults to the cache after the store is reset', done => {

@@ -400,3 +452,3 @@ const mutation = gql`

.then(({ data }) => {
expect({ ...data }).toEqual({ foo: 'bar' });
expect({ ...data }).toMatchObject({ foo: 'bar' });
})

@@ -409,3 +461,3 @@ .catch(done.fail);

.then(({ data }) => {
expect({ ...data }).toEqual({ foo: 'woo' });
expect({ ...data }).toMatchObject({ foo: 'woo' });
})

@@ -416,3 +468,3 @@ //should be default after this reset call

.then(({ data }) => {
expect({ ...data }).toEqual({ foo: 'bar' });
expect({ ...data }).toMatchObject({ foo: 'bar' });
done();

@@ -530,3 +582,3 @@ })

it('returns the Query result after resetStore', done => {
it('returns the Query result after resetStore', async done => {
const stateLink = withClientState({

@@ -555,3 +607,3 @@ cache,

const client = createClient(stateLink);
client.mutate({ mutation: plusMutation });
await client.mutate({ mutation: plusMutation });
expect(cache.readQuery({ query: counterQuery })).toMatchObject({

@@ -561,7 +613,9 @@ counter: 11,

client.mutate({ mutation: plusMutation });
await client.mutate({ mutation: plusMutation });
expect(cache.readQuery({ query: counterQuery })).toMatchObject({
counter: 12,
});
expect(client.query({ query: counterQuery })).resolves.toMatchObject({
await expect(
client.query({ query: counterQuery }),
).resolves.toMatchObject({
data: { counter: 12 },

@@ -742,3 +796,3 @@ });

client.resetStore() as Promise<null>;
client.resetStore();
});

@@ -810,3 +864,3 @@ });

try {
expect({ ...data }).toEqual({ count: 0, lastCount: 1 });
expect({ ...data }).toMatchObject({ count: 0, lastCount: 1 });
} catch (e) {

@@ -820,3 +874,3 @@ done.fail(e);

try {
expect({ ...data }).toEqual({ count: 2, lastCount: 1 });
expect({ ...data }).toMatchObject({ count: 2, lastCount: 1 });
} catch (e) {

@@ -829,3 +883,3 @@ done.fail(e);

try {
expect({ ...data }).toEqual({ count: 1, lastCount: 1 });
expect({ ...data }).toMatchObject({ count: 1, lastCount: 1 });
} catch (e) {

@@ -886,3 +940,3 @@ done.fail(e);

if (count === 1) {
expect({ ...data }).toEqual({ todos: [] });
expect({ ...data }).toMatchObject({ todos: [] });
client.mutate({

@@ -896,3 +950,3 @@ mutation,

} else if (count === 2) {
expect(data.todos.map(x => ({ ...x }))).toEqual([
expect(data.todos.map(x => ({ ...x }))).toMatchObject([
{

@@ -899,0 +953,0 @@ title: 'Apollo Client 2.0',

@@ -5,3 +5,2 @@ import gql from 'graphql-tag';

import { print } from 'graphql/language/printer';
import { parse } from 'graphql/language/parser';

@@ -19,21 +18,2 @@ import { withClientState } from '../';

const aliasedQuery = gql`
query Test {
fie: foo @client {
bar
}
}
`;
const doubleQuery = gql`
query Double {
foo @client {
bar
}
bar @client {
foo
}
}
`;
const mixedQuery = gql`

@@ -50,11 +30,2 @@ query Mixed {

const data = {
foo: { bar: true },
};
const doubleData = {
foo: { bar: true },
bar: { foo: false },
};
const resolvers = {

@@ -74,3 +45,7 @@ Query: {

execute(client.concat(nextLink), { query }).subscribe(result => {
expect(result.data).toEqual({ foo: { bar: true } });
try {
expect(result.data).toEqual({ foo: { bar: true } });
} catch (error) {
done.fail(error);
}
done();

@@ -82,12 +57,16 @@ }, done.fail);

const nextLink = new ApolloLink(operation => {
expect(operation.getContext()).toMatchSnapshot();
expect(print(operation.query)).toEqual(
print(gql`
query Mixed {
bar {
foo
try {
expect(operation.getContext()).toMatchSnapshot();
expect(print(operation.query)).toEqual(
print(gql`
query Mixed {
bar {
foo
}
}
}
`),
);
`),
);
} catch (error) {
done.fail(error);
}
return Observable.of({ data: { bar: { foo: true } } });

@@ -113,3 +92,7 @@ });

execute(client, { query }).subscribe(({ data }) => {
expect(data).toEqual({ foo: { bar: true } });
try {
expect(data).toEqual({ foo: { bar: true } });
} catch (error) {
done.fail(error);
}
done();

@@ -135,3 +118,7 @@ }, done.fail);

execute(client.concat(sample), { query }).subscribe(({ data }) => {
expect(data).toEqual({ foo: { bar: true }, bar: { baz: true } });
try {
expect(data).toEqual({ foo: { bar: true }, bar: { baz: true } });
} catch (error) {
done.fail(error);
}
done();

@@ -189,3 +176,7 @@ }, done.fail);

execute(client, { query, variables: { id: 1 } }).subscribe(({ data }) => {
expect(data).toEqual({ foo: { bar: 1 } });
try {
expect(data).toEqual({ foo: { bar: 1 } });
} catch (error) {
done.fail(error);
}
done();

@@ -195,45 +186,26 @@ }, done.fail);

it('runs resolvers for missing client queries with aliased field', done => {
it('passes context to client resolvers', done => {
const query = gql`
query Aliased {
query WithContext {
foo @client {
bar
}
baz: bar {
foo
}
}
`;
const sample = new ApolloLink(() =>
//The server takes care of the aliasing, so returns baz, not bar
Observable.of({ data: { baz: { foo: true } } }),
);
const client = withClientState({
resolvers,
});
execute(client.concat(sample), { query }).subscribe(({ data }) => {
try {
expect(data).toEqual({ foo: { bar: true }, baz: { foo: true } });
} catch (e) {
done.fail(e);
}
done();
}, done.fail);
});
it('runs resolvers for client queries when aliases are in use on the @client-tagged node', done => {
const client = withClientState({
resolvers: {
Query: {
foo: () => ({ bar: true }),
fie: () => {
done.fail(
"Called the resolver using the alias' name, instead of the correct resolver name.",
);
},
foo: () => ({ __typename: 'Foo' }),
},
Foo: {
bar: (data, _, { id }) => id,
},
},
});
execute(client, { query: aliasedQuery }).subscribe(({ data }) => {
expect(data).toEqual({ fie: { bar: true } });
execute(client, { query, context: { id: 1 } }).subscribe(({ data }) => {
try {
expect(data).toEqual({ foo: { bar: 1 } });
} catch (error) {
done.fail(error);
}
done();

@@ -243,3 +215,3 @@ }, done.fail);

it('passes context to client resolvers', done => {
it('calls resolvers on each request if the prop is a function', done => {
const query = gql`

@@ -252,4 +224,6 @@ query WithContext {

`;
const client = withClientState({
resolvers: {
const resolversSpy = jest.fn();
const resolvers = () => {
resolversSpy();
return {
Query: {

@@ -259,10 +233,22 @@ foo: () => ({ __typename: 'Foo' }),

Foo: {
bar: (data, _, { id }) => id,
bar: () => 1,
},
},
};
};
const client = withClientState({
resolvers,
});
execute(client, { query, context: { id: 1 } }).subscribe(({ data }) => {
// once
execute(client, { query }).subscribe(({ data }) => {
expect(data).toEqual({ foo: { bar: 1 } });
done();
// twice
execute(client, { query }).subscribe(({ data }) => {
expect(data).toEqual({ foo: { bar: 1 } });
expect(resolversSpy).toHaveBeenCalledTimes(2);
done();
}, done.fail);
}, done.fail);
});

@@ -46,3 +46,3 @@ import gql from 'graphql-tag';

.then(({ data }) => {
expect({ ...data }).toEqual({ field: 1 });
expect({ ...data }).toMatchObject({ field: 1 });
});

@@ -49,0 +49,0 @@ });

@@ -9,8 +9,13 @@ import {

import { ApolloCache } from 'apollo-cache';
import { DocumentNode } from 'graphql';
import { hasDirectives, getMainDefinition } from 'apollo-utilities';
import { graphql } from 'graphql-anywhere/lib/async';
import { removeClientSetsFromDocument } from './utils';
import * as Async from 'graphql-anywhere/lib/async';
const { graphql } = Async;
import { FragmentMatcher } from 'graphql-anywhere';
import { removeClientSetsFromDocument, normalizeTypeDefs } from './utils';
const capitalizeFirstLetter = str => str.charAt(0).toUpperCase() + str.slice(1);

@@ -20,5 +25,6 @@

cache?: ApolloCache<any>;
resolvers: any;
resolvers: any | (() => any);
defaults?: any;
typeDefs?: string | string[];
typeDefs?: string | string[] | DocumentNode | DocumentNode[];
fragmentMatcher?: FragmentMatcher;
};

@@ -29,3 +35,3 @@

) => {
const { resolvers, defaults, cache, typeDefs } = clientStateConfig;
const { defaults, cache, typeDefs, fragmentMatcher } = clientStateConfig;
if (cache && defaults) {

@@ -48,7 +54,5 @@ cache.writeData({ data: defaults });

const directives = 'directive @client on FIELD';
const definition =
typeof typeDefs === 'string'
? typeDefs
: typeDefs.map(typeDef => typeDef.trim()).join('\n');
const definition = normalizeTypeDefs(typeDefs);
operation.setContext(({ schemas = [] }) => ({

@@ -63,2 +67,6 @@ schemas: schemas.concat([{ definition, directives }]),

const resolvers =
typeof clientStateConfig.resolvers === 'function'
? clientStateConfig.resolvers()
: clientStateConfig.resolvers;
const server = removeClientSetsFromDocument(operation.query);

@@ -72,9 +80,21 @@ const { query } = operation;

const resolver = (fieldName, rootValue = {}, args, context, info) => {
//resultKey is where data under the field name is ultimately returned by the server
//https://github.com/apollographql/apollo-client/tree/master/packages/graphql-anywhere#resolver-info
const fieldValue = rootValue[info.resultKey];
const { resultKey } = info;
//If fieldValue is defined, server returned a value
if (fieldValue !== undefined) return fieldValue;
// rootValue[fieldName] is where the data is stored in the "canonical model"
// rootValue[info.resultKey] is where the user wants the data to be.
// If fieldName != info.resultKey -- then GraphQL Aliases are in play
// See also:
// - https://github.com/apollographql/apollo-client/tree/master/packages/graphql-anywhere#resolver-info
// - https://github.com/apollographql/apollo-link-rest/pull/113
// Support GraphQL Aliases!
const aliasedNode = rootValue[resultKey];
const preAliasingNode = rootValue[fieldName];
const aliasNeeded = resultKey !== fieldName;
// If aliasedValue is defined, some other link or server already returned a value
if (aliasedNode !== undefined || preAliasingNode !== undefined) {
return aliasedNode || preAliasingNode;
}
// Look for the field in the custom resolver map

@@ -86,26 +106,39 @@ const resolverMap = resolvers[(rootValue as any).__typename || type];

}
//TODO: the proper thing to do here is throw an error saying to
//add `client.onResetStore(link.writeDefaults);`
//waiting on https://github.com/apollographql/apollo-client/pull/3010
//Currently with nested fields, this sort of return does not work
return defaults[fieldName];
// TODO: the proper thing to do here is throw an error saying to
// add `client.onResetStore(link.writeDefaults);`
// waiting on https://github.com/apollographql/apollo-client/pull/3010
return (
// Support nested fields
(aliasNeeded ? aliasedNode : preAliasingNode) ||
(defaults || {})[fieldName]
);
};
if (server) operation.query = server;
const obs =
server && forward
? forward(operation)
: Observable.of({
data: {},
});
return new Observable(observer => {
if (server) operation.query = server;
const obs =
server && forward
? forward(operation)
: Observable.of({
data: {},
});
const observerErrorHandler = observer.error.bind(observer);
const sub = obs.subscribe({
// Works around race condition between completion and graphql execution
// finishing. If complete is called during the graphql call, we will
// miss out on the result, since the observer will have completed
let complete = false;
let handlingNext = false;
obs.subscribe({
next: ({ data, errors }) => {
const observerErrorHandler = observer.error.bind(observer);
const context = operation.getContext();
handlingNext = true;
//data is from the server and provides the root value to this GraphQL resolution
//when there is no resolver, the data is taken from the context
graphql(resolver, query, data, context, operation.variables)
graphql(resolver, query, data, context, operation.variables, {
fragmentMatcher,
})
.then(nextData => {

@@ -116,12 +149,17 @@ observer.next({

});
observer.complete();
if (complete) {
observer.complete();
}
handlingNext = false;
})
.catch(observerErrorHandler);
},
error: observerErrorHandler,
error: observer.error.bind(observer),
complete: () => {
if (!handlingNext) {
observer.complete();
}
complete = true;
},
});
return () => {
if (sub) sub.unsubscribe();
};
});

@@ -128,0 +166,0 @@ }

@@ -1,7 +0,6 @@

import { DocumentNode, DirectiveNode } from 'graphql';
// importing print is a reasonable thing to do, since Apollo Link Http requires
// it to be present
import { DocumentNode, DirectiveNode, print } from 'graphql';
import {
checkDocument,
removeDirectivesFromDocument,
} from 'apollo-utilities';
import { checkDocument, removeDirectivesFromDocument } from 'apollo-utilities';

@@ -32,1 +31,12 @@ const connectionRemoveConfig = {

}
export function normalizeTypeDefs(
typeDefs: string | string[] | DocumentNode | DocumentNode[],
) {
const defs = Array.isArray(typeDefs) ? typeDefs : [typeDefs];
return defs
.map(typeDef => (typeof typeDef === 'string' ? typeDef : print(typeDef)))
.map(str => str.trim())
.join('\n');
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc