Skip to content

Commit f7f5ee0

Browse files
committed
Change ManyLink to Collection
1 parent 28d145a commit f7f5ee0

File tree

6 files changed

+148
-121
lines changed

6 files changed

+148
-121
lines changed

src/Collection.js

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import Link from './Link';
2+
import { TYPENAME } from './constants';
3+
4+
const transformEntitiesToLinks = (value) => {
5+
// eslint-disable-next-line no-use-before-define
6+
if (value instanceof Collection || value instanceof Link) {
7+
return value;
8+
}
9+
10+
if (Array.isArray(value)) {
11+
// eslint-disable-next-line no-use-before-define
12+
return new Collection(value);
13+
}
14+
15+
if (value === null || typeof value !== 'object') {
16+
return value;
17+
}
18+
19+
if (TYPENAME in value) {
20+
return new Link(value);
21+
}
22+
23+
const item = {};
24+
25+
Object.entries(value).forEach(([k, v]) => {
26+
item[k] = transformEntitiesToLinks(v);
27+
});
28+
29+
return item;
30+
};
31+
32+
const convertToLinks = (values) => {
33+
const items = Array.isArray(values) ? values : [values];
34+
35+
return items.map(transformEntitiesToLinks);
36+
};
37+
38+
const removeDuplicateLinks = (currentRefs, items) =>
39+
currentRefs.filter((refLink) => !items.some((link) => refLink.equals(link)));
40+
41+
export default class Collection {
42+
items;
43+
44+
constructor(values = []) {
45+
this.items = convertToLinks(values);
46+
}
47+
48+
static fromNative(items) {
49+
const instance = new Collection();
50+
instance.items = items;
51+
52+
return instance;
53+
}
54+
55+
prepend(values) {
56+
this.items = [...convertToLinks(values), ...this.items];
57+
58+
return this;
59+
}
60+
61+
append(values) {
62+
this.items = [...this.items, ...convertToLinks(values)];
63+
64+
return this;
65+
}
66+
67+
syncPrepend(values) {
68+
const items = convertToLinks(values);
69+
const filteredLinks = removeDuplicateLinks(this.items, items);
70+
71+
this.items = [...items, ...filteredLinks];
72+
73+
return this;
74+
}
75+
76+
syncAppend(values) {
77+
const items = convertToLinks(values);
78+
const filteredLinks = removeDuplicateLinks(this.items, items);
79+
80+
this.items = [...filteredLinks, ...items];
81+
82+
return this;
83+
}
84+
85+
detach(values) {
86+
const items = convertToLinks(values);
87+
this.items = removeDuplicateLinks(this.items, items);
88+
89+
return this;
90+
}
91+
92+
all() {
93+
return this.items;
94+
}
95+
96+
toNative() {
97+
return this.all();
98+
}
99+
}

src/Link.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ export default class Link {
1818
return instance;
1919
}
2020

21+
equals(link) {
22+
return this.ref[0] === link.ref[0] && this.ref[1] === link.ref[1];
23+
}
24+
2125
toNative() {
2226
return this.ref;
2327
}

src/ManyLink.js

Lines changed: 0 additions & 75 deletions
This file was deleted.

src/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ import createNode from './container/createNode';
33
import GraphQLError from './errors/GraphQLError';
44
import HttpError from './errors/HttpError';
55
import Resource from './resources/Resource';
6+
import Collection from './Collection';
67
import createHttpClient from './createHttpClient';
78
import key from './key';
89
import Link from './Link';
9-
import ManyLink from './ManyLink';
1010
import ref from './ref';
1111
import Transporter from './Transporter';
1212
import TransporterProvider from './TransporterProvider';
@@ -21,10 +21,10 @@ export {
2121
GraphQLError,
2222
HttpError,
2323
Resource,
24+
Collection,
2425
createHttpClient,
2526
key,
2627
Link,
27-
ManyLink,
2828
ref,
2929
Transporter,
3030
TransporterProvider,

src/network/ValueCaster.js

Lines changed: 41 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,71 @@
11
import Link from '../Link';
2-
import ManyLink from '../ManyLink';
3-
import { REF_KEY } from '../constants';
2+
import Collection from '../Collection';
3+
import { REF_KEY, TYPENAME } from '../constants';
4+
5+
// Native format:
6+
// - Only uses arrays and { ref: [typename, id] } refs
7+
//
8+
// User (non-native) format:
9+
// - Uses Collection instances instead of arrays
10+
// - Uses Link instances instead of { ref: [typename, id] } refs
11+
// - Also allows { __typename, id, ... } objects as refs
412

513
const isDate = (v) => Object.prototype.toString.call(v) === '[object Date]';
614

715
export default class ValueCaster {
816
static fromNative(value) {
917
if (Array.isArray(value)) {
10-
return value.map((v) => ValueCaster.fromNative(v));
18+
return Collection.fromNative(value.map((v) => ValueCaster.fromNative(v)));
1119
}
1220

13-
if (typeof value === 'object') {
14-
if (REF_KEY in value) {
15-
const ref = value[REF_KEY];
16-
17-
if (ref.length === 2 && !Array.isArray(ref[0])) {
18-
return Link.fromNative(ref);
19-
}
20-
21-
return ManyLink.fromNative(ref);
22-
}
21+
if (value === null || typeof value !== 'object') {
22+
return value;
23+
}
2324

24-
const result = {};
25+
if (REF_KEY in value) {
26+
return Link.fromNative(value[REF_KEY]);
27+
}
2528

26-
Object.entries(value).forEach(([k, v]) => {
27-
result[k] = ValueCaster.fromNative(v);
28-
});
29+
const item = {};
2930

30-
return result;
31-
}
31+
Object.entries(value).forEach(([k, v]) => {
32+
item[k] = ValueCaster.fromNative(v);
33+
});
3234

33-
return value;
35+
return item;
3436
}
3537

3638
static toNative(value) {
37-
if (value instanceof Link || value instanceof ManyLink) {
38-
return {
39-
[REF_KEY]: value.toNative(),
40-
};
41-
}
42-
4339
if (isDate(value)) {
4440
return value.toISOString();
4541
}
4642

43+
if (value instanceof Collection) {
44+
return value.toNative().map((v) => ValueCaster.toNative(v));
45+
}
46+
4747
if (Array.isArray(value)) {
4848
return value.map((v) => ValueCaster.toNative(v));
4949
}
5050

51-
if (value && typeof value === 'object') {
52-
const result = {};
51+
if (value === null || typeof value !== 'object') {
52+
return value;
53+
}
5354

54-
Object.entries(value).forEach(([k, v]) => {
55-
result[k] = ValueCaster.toNative(v);
56-
});
55+
if (value instanceof Link) {
56+
return { [REF_KEY]: value.toNative() };
57+
}
5758

58-
return result;
59+
if (TYPENAME in value) {
60+
return { [REF_KEY]: new Link(value).toNative() };
5961
}
6062

61-
return value;
63+
const item = {};
64+
65+
Object.entries(value).forEach(([k, v]) => {
66+
item[k] = ValueCaster.toNative(v);
67+
});
68+
69+
return item;
6270
}
6371
}

src/network/traverseAST.js

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -164,21 +164,12 @@ const handleValue = (selectionSet, value, context, path) => {
164164
return handleSelectionSet(selectionSet, value, context, path);
165165
}
166166

167-
const ref = value[REF_KEY];
168167
const { handleLink } = context;
169168

170169
// TODO: Apply connections
171-
// const entity = handleConnection(ref ? data.get(...ref) : value);
170+
// const entity = handleConnection(value[REF_KEY] ? data.get(...value[REF_KEY]) : value);
172171

173-
if (ref.length === 2 && !Array.isArray(ref[0])) {
174-
return handleLink(handleRef(selectionSet, ref, context, path));
175-
}
176-
177-
return handleLink(
178-
ref.map((refItem, refKey) =>
179-
handleRef(selectionSet, refItem, context, [...path, refKey]),
180-
),
181-
);
172+
return handleLink(handleRef(selectionSet, value[REF_KEY], context, path));
182173
};
183174

184175
export default function traverseAST(

0 commit comments

Comments
 (0)