Skip to content

Commit

Permalink
Throw when querying non-scalar objects without a selection set.
Browse files Browse the repository at this point in the history
This would have helped diagnose the cause of #4025, which is that query
fields with non-scalar types (or list-of-non-scalar types) must have a
selection set.

In other words, given a schema like

  type Query {
    selectedMessageList: [SelectedMessage]
  }

a query like

  query getSelectedMessageList {
    selectedMessageList @client
  }

is invalid since it doesn't specify which fields of the SelectedMessage
objects should be included in the list.
  • Loading branch information
benjamn committed Oct 23, 2018
1 parent f880c2c commit 16d14db
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 18 deletions.
54 changes: 37 additions & 17 deletions packages/apollo-cache-inmemory/src/readFromStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -401,18 +401,6 @@ export class StoreReader {
info,
);

// Handle all scalar types here
if (!field.selectionSet) {
return readStoreResult;
}

// From here down, the field has a selection set, which means it's trying to
// query a GraphQLObjectType
if (readStoreResult.result == null) {
// Basically any field in a GraphQL response can be null, or missing
return readStoreResult;
}

function handleMissing<T>(res: ExecResult<T>): ExecResult<T> {
let missing: ExecResultMissingField[] = null;

Expand Down Expand Up @@ -440,6 +428,19 @@ export class StoreReader {
));
}

// Handle all scalar types here
if (!field.selectionSet) {
assertSelectionSetForIdValue(field, readStoreResult.result);
return readStoreResult;
}

// From here down, the field has a selection set, which means it's trying to
// query a GraphQLObjectType
if (readStoreResult.result == null) {
// Basically any field in a GraphQL response can be null, or missing
return readStoreResult;
}

// Returned value is an object, and the query has a sub-selection. Recurse.
return handleMissing(this.executeSelectionSet({
selectionSet: field.selectionSet,
Expand Down Expand Up @@ -476,17 +477,36 @@ export class StoreReader {
}

// This is an object, run the selection set on it
return handleMissing(this.executeSelectionSet({
selectionSet: field.selectionSet,
rootValue: item,
execContext,
}));
if (field.selectionSet) {
return handleMissing(this.executeSelectionSet({
selectionSet: field.selectionSet,
rootValue: item,
execContext,
}));
}

assertSelectionSetForIdValue(field, item);

return item;
});

return { result, missing };
}
}

function assertSelectionSetForIdValue(
field: FieldNode,
value: any,
) {
if (!field.selectionSet && isIdValue(value)) {
throw new Error(
`Missing selection set for object of type ${
value.typename
} returned by query field ${field.name.value}`
);
}
}

function defaultFragmentMatcher() {
return true;
}
Expand Down
4 changes: 3 additions & 1 deletion packages/apollo-utilities/src/storeUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,9 @@ export function isInlineFragment(
}

export function isIdValue(idObject: StoreValue): idObject is IdValue {
return idObject && (idObject as IdValue | JsonValue).type === 'id';
return idObject &&
(idObject as IdValue | JsonValue).type === 'id' &&
typeof (idObject as IdValue).generated === 'boolean';
}

export type IdConfig = {
Expand Down

0 comments on commit 16d14db

Please sign in to comment.