Skip to content

Commit

Permalink
(graphcache) - Enable more cache.keyOfEntity parent cases and depreca…
Browse files Browse the repository at this point in the history
…te cache.resolveFieldByKey (#1219)

* Enable store.keyOfEntity to handle more parent cases

* Add test for store.keyOfEntity with missing __typename field

* Deprecate cache.resolveFieldByKey in favor of cache.resolve

* Add changesets

* Replace cache.resolveFieldByKey in extras sources
  • Loading branch information
kitten authored Dec 10, 2020
1 parent fa6fb5a commit 4327263
Show file tree
Hide file tree
Showing 9 changed files with 43 additions and 30 deletions.
5 changes: 5 additions & 0 deletions .changeset/tough-keys-visit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@urql/exchange-graphcache': patch
---

Update `cache.resolve(parent, ...)` case to enable _even more_ cases, for instance where `parent.__typename` isn't set yet. This was intended to be enabled in the previous patch but has been forgotten.
5 changes: 5 additions & 0 deletions .changeset/wild-pandas-vanish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@urql/exchange-graphcache': patch
---

Deprecate `cache.resolveFieldByKey` in favour of `cache.resolve`, which functionally was already able to do the same.
11 changes: 6 additions & 5 deletions docs/api/graphcache.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,15 +250,16 @@ an entity key, which makes it possible to pass a key from `cache.keyOfEntity` or
> be dangerous to use in some cases. It's a good idea to make sure first whether the field you're
> reading will be a key or a value.
There's an alternative method, `cache.resolveFieldByKey` which accepts a field key that may be
generated using `cache.keyOfField`.
The `cache.resolve` method may also be called with a field key as generated by `cache.keyOfField`.

```js
cache.resolveFieldByKey({ __typename: 'Query' }, cache.keyOfField('todo', { id: 1 })); // 'Todo:1'
cache.resolve({ __typename: 'Query' }, cache.keyOfField('todo', { id: 1 })); // 'Todo:1'
```

This specialized method is likely only going to be useful in combination with
[`cache.inspectFields`](#inspectfields).
This specialized case is likely only going to be useful in combination with
[`cache.inspectFields`](#inspectfields). Previously a specialised method existed for this
case specifically and was called `cache.resolveFieldByKey`, which is now deprecated, since
`cache.resolve` may be called with a field key and no extra arguments.

### inspectFields

Expand Down
2 changes: 1 addition & 1 deletion exchanges/graphcache/default-storage/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"./package.json": "./package.json"
},
"dependencies": {
"@urql/core": ">=1.15.2",
"@urql/core": ">=1.16.0",
"wonka": "^4.0.14"
}
}
2 changes: 1 addition & 1 deletion exchanges/graphcache/src/extras/relayPagination.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ const getPage = (
entityKey: string,
fieldKey: string
): Page | null => {
const link = ensureKey(cache.resolveFieldByKey(entityKey, fieldKey));
const link = ensureKey(cache.resolve(entityKey, fieldKey));
if (!link) return null;

const typename = cache.resolve(link, '__typename') as string;
Expand Down
2 changes: 1 addition & 1 deletion exchanges/graphcache/src/extras/simplePagination.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export const simplePagination = ({
continue;
}

const links = cache.resolveFieldByKey(entityKey, fieldKey) as string[];
const links = cache.resolve(entityKey, fieldKey) as string[];
const currentOffset = args[offsetArgument];

if (
Expand Down
4 changes: 4 additions & 0 deletions exchanges/graphcache/src/store/store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,10 @@ describe('Store with OptimisticMutationConfig', () => {

expect(store.keyOfEntity(randomData)).toBe(context.parentKey);
expect(store.keyOfEntity({})).not.toBe(context.parentKey);

// Should work without a __typename field
delete (randomData as any).__typename;
expect(store.keyOfEntity(randomData)).toBe(context.parentKey);
});

it('should resolve with a key as first argument', () => {
Expand Down
40 changes: 19 additions & 21 deletions exchanges/graphcache/src/store/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,45 +105,43 @@ export class Store implements Cache {
keyOfField = keyOfField;

keyOfEntity(data: Data | null | string) {
if (data == null || typeof data === 'string') return data || null;
const { __typename: typename, id, _id } = data;
if (!typename) return null;
if (this.rootNames[typename]) return typename;

// In resolvers and updaters we may have a specific parent
// object available that can be used to skip to a specific parent
// key directly without looking at its incomplete properties
if (contextRef.current && data === contextRef.current.parent)
return contextRef.current!.parentKey;

if (data == null || typeof data === 'string') return data || null;
if (!data.__typename) return null;
if (this.rootNames[data.__typename]) return data.__typename;

let key: string | null | void;
if (this.keys[typename]) {
key = this.keys[typename](data);
} else if (id !== undefined && id !== null) {
key = `${id}`;
} else if (_id !== undefined && _id !== null) {
key = `${_id}`;
if (this.keys[data.__typename]) {
key = this.keys[data.__typename](data);
} else if (data.id != null) {
key = `${data.id}`;
} else if (data._id != null) {
key = `${data._id}`;
}

return key ? `${typename}:${key}` : null;
return key ? `${data.__typename}:${key}` : null;
}

resolveFieldByKey(entity: Data | string | null, fieldKey: string): DataField {
resolve(
entity: Data | string | null,
field: string,
args?: Variables
): DataField {
const fieldKey = keyOfField(field, args);
const entityKey = this.keyOfEntity(entity);
if (!entityKey) return null;
const fieldValue = InMemoryData.readRecord(entityKey, fieldKey);
if (fieldValue !== undefined) return fieldValue;
const link = InMemoryData.readLink(entityKey, fieldKey);
return link ? link : null;
return link || null;
}

resolve(
entity: Data | string | null,
field: string,
args?: Variables
): DataField {
return this.resolveFieldByKey(entity, keyOfField(field, args));
}
resolveFieldByKey = this.resolve;

invalidate(entity: Data | string | null, field?: string, args?: Variables) {
const entityKey = this.keyOfEntity(entity);
Expand Down
2 changes: 1 addition & 1 deletion exchanges/graphcache/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export interface Cache {
args?: Variables
): DataField;

/** resolveFieldByKey() returns a field's value on an entity, given that field's key */
/** @deprecated use resolve() instead */
resolveFieldByKey(entity: Data | string | null, fieldKey: string): DataField;

/** inspectFields() retrieves all known fields for a given entity */
Expand Down

0 comments on commit 4327263

Please sign in to comment.