Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose a way to read an object given an id #476

Closed
wants to merge 9 commits into from
25 changes: 22 additions & 3 deletions src/data/readFromStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,28 @@ import {
NormalizedCache,
} from './store';

// import {
// printAST,
// } from './debug';
// Read an object from the store given that object's id.
// This function performs a deep read, i.e. it will read the fields of the object
// from the normalized store structure as well.
export function readObjectByIdFromStore({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason we need this function anymore? Seems like this is just a clone of readFragmentFromStore?

Copy link
Contributor Author

@Poincare Poincare Aug 2, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stubailo

I currently use it for all of the unit tests. The main point is to give it a name that reflects the name exposed to the app developer through Apollo Client. Maybe readFragmentFromStore might do something slightly different in the future so having the unit tests call readObjectByIdFromStore instead (which is a name that describes what is happening) may be a good idea.

store,
id,
fragment,
variables,
}: {
store: NormalizedCache,
id: string,
fragment: Document,
variables?: Object,
}): any {
return readFragmentFromStore({
store,
fragment,
rootId: id,
variables,
});
}


export function readQueryFromStore({
store,
Expand Down
15 changes: 15 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ import {
ApolloReducerConfig,
} from './store';

import {
readObjectByIdFromStore,
} from './data/readFromStore';

import {
QueryManager,
} from './QueryManager';
Expand Down Expand Up @@ -294,6 +298,17 @@ export default class ApolloClient {
}));
};

// Given a particular id, this method returns a deep read in the normalized
// store starting from that id. The `fragment` is used to select the fields of the
// object with the given id that will be returned.
public readObjectById(id: string, fragment: Document) {
return readObjectByIdFromStore({
store: this.store.getState().apollo.data,
id,
fragment,
});
}

private setStore = (store: ApolloStore) => {
// ensure existing store has apolloReducer
if (isUndefined(store.getState()[this.reduxRootKey])) {
Expand Down
133 changes: 133 additions & 0 deletions test/client.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as chai from 'chai';
const { assert } = chai;
import * as sinon from 'sinon';
import * as _ from 'lodash';

import ApolloClient, {
createFragment,
Expand All @@ -15,6 +16,7 @@ import {
GraphQLError,
OperationDefinition,
GraphQLResult,
Document,
} from 'graphql';

import {
Expand Down Expand Up @@ -1594,4 +1596,135 @@ describe('client', () => {
done();
});
});

describe('read by id', () => {
const setupClient = (primeQuery: Document, primeResult: Object) => {
return new ApolloClient({
networkInterface: mockNetworkInterface({
request: { query: primeQuery },
result: { data: primeResult },
}),
});
};

it('should be able to read an object from the store by its id', (done) => {
const primeQuery = gql`
query {
person {
name
}
}`;
const primeResult = {
person: {
name: 'John Smith',
},
};
const client = setupClient(primeQuery, primeResult);
client.query({ query: primeQuery }).then((result) => {
const person = client.readObjectById('$ROOT_QUERY.person', gql`
fragment details on Person {
name
}`);
assert.deepEqual(person, primeResult.person);
done();
});
});

it('should be able to limit the fields it reads from the store by id', (done) => {
const primeQuery = gql`
query {
person {
firstName
lastName
}
}`;
const primeResult = {
person: {
firstName: 'John',
lastName: 'Smith',
},
};
const client = setupClient(primeQuery, primeResult);
client.query({ query: primeQuery }).then((result) => {
const person = client.readObjectById('$ROOT_QUERY.person', gql`
fragment details on Person {
firstName
}`);
assert.deepEqual(person, _.omit(primeResult.person, 'lastName'));
done();
});
});

it('should be able to pick data from multiple past writes', (done) => {
const primeQuery = gql`
query {
person {
name
friend(id: 1) {
name
}
}
}`;

const primeResult = {
person: {
name: 'John Smith',
friend: {
name: 'Jane Smith',
},
},
};
const secondaryQuery = gql`
query {
person {
name
friend(id: 2) {
name
}
}
}`;
const secondaryResult = {
person: {
name: 'John Smith',
friend: {
name: 'Jack Smith',
},
},
};
const client = new ApolloClient({
networkInterface: mockNetworkInterface(
{
request: { query: primeQuery },
result: { data: primeResult },
},
{
request: { query: secondaryQuery },
result: { data: secondaryResult },
}
),
});
client.query({ query: primeQuery }).then(() => {
client.query({ query: secondaryQuery }).then(() => {
let person = client.readObjectById('$ROOT_QUERY.person', gql`
fragment details on Person {
name
friend(id: 2) {
name
}
}`);
assert.deepEqual(person, secondaryResult.person);

person = client.readObjectById('$ROOT_QUERY.person', gql`
fragment details on Person {
name
friend(id: 1) {
name
}
}`);
assert.deepEqual(person, primeResult.person);
done();
});
});
});
});
});
Loading