@projectcaluma/ember-testing 9.0.0 → 10.2.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/addon/mirage-graphql/filters/answer.js +13 -0
- package/addon/mirage-graphql/filters/base.js +47 -20
- package/addon/mirage-graphql/filters/question.js +1 -3
- package/addon/mirage-graphql/filters/work-item.js +27 -0
- package/addon/mirage-graphql/handler.js +25 -7
- package/addon/mirage-graphql/index.js +24 -3
- package/addon/mirage-graphql/mocks/answer.js +8 -33
- package/addon/mirage-graphql/mocks/base.js +119 -47
- package/addon/mirage-graphql/mocks/form.js +25 -100
- package/addon/mirage-graphql/mocks/question.js +17 -119
- package/addon/mirage-graphql/mocks/work-item.js +72 -0
- package/addon/mirage-graphql/schema.graphql +121 -8
- package/addon/scenarios/distribution.js +287 -0
- package/addon-mirage-support/factories/answer.js +2 -0
- package/addon-mirage-support/factories/case.js +6 -5
- package/addon-mirage-support/factories/document.js +4 -1
- package/addon-mirage-support/factories/file.js +6 -5
- package/addon-mirage-support/factories/form.js +3 -0
- package/addon-mirage-support/factories/format-validator.js +3 -0
- package/addon-mirage-support/factories/option.js +3 -0
- package/addon-mirage-support/factories/question.js +19 -0
- package/addon-mirage-support/factories/task.js +6 -5
- package/addon-mirage-support/factories/work-item.js +7 -5
- package/addon-mirage-support/factories/workflow.js +9 -0
- package/addon-mirage-support/models/answer.js +3 -2
- package/addon-mirage-support/models/case.js +1 -0
- package/addon-mirage-support/models/document.js +1 -0
- package/addon-mirage-support/models/form.js +1 -1
- package/addon-mirage-support/models/question.js +4 -2
- package/addon-mirage-support/models/work-item.js +2 -0
- package/index.js +1 -1
- package/package.json +19 -15
- package/addon/mirage-graphql/mocks/case.js +0 -9
- package/addon/mirage-graphql/mocks/task.js +0 -55
- package/addon/mirage-graphql/resolvers/index.js +0 -16
- package/addon/mirage-graphql/serializers/answer.js +0 -14
- package/addon/mirage-graphql/serializers/base.js +0 -13
- package/addon/mirage-graphql/serializers/case.js +0 -11
- package/addon/mirage-graphql/serializers/document.js +0 -11
- package/addon/mirage-graphql/serializers/file.js +0 -12
- package/addon/mirage-graphql/serializers/form.js +0 -11
- package/addon/mirage-graphql/serializers/question.js +0 -14
- package/addon/mirage-graphql/serializers/task.js +0 -16
- package/addon/mirage-graphql/serializers/work-item.js +0 -11
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import BaseFilter from "@projectcaluma/ember-testing/mirage-graphql/filters/base";
|
|
2
|
+
|
|
3
|
+
export default class extends BaseFilter {
|
|
4
|
+
questions(records, value, { invert = false }) {
|
|
5
|
+
return records.filter(
|
|
6
|
+
(record) => invert !== value.includes(record.questionId)
|
|
7
|
+
);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
question(records, value, { invert = false }) {
|
|
11
|
+
return this.questions(records, [value], { invert });
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -1,39 +1,66 @@
|
|
|
1
|
+
import { camelize } from "@ember/string";
|
|
2
|
+
|
|
1
3
|
export default class {
|
|
2
|
-
constructor(type
|
|
4
|
+
constructor(type) {
|
|
3
5
|
this.type = type;
|
|
4
|
-
this.collection = collection;
|
|
5
|
-
this.db = db;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
_getFilterFns(
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
_getFilterFns(rawFilters) {
|
|
9
|
+
const filters = Array.isArray(rawFilters)
|
|
10
|
+
? // new format
|
|
11
|
+
rawFilters
|
|
12
|
+
.filter((filter) => Object.keys(filter).length !== 0) // filter out empty filters
|
|
13
|
+
.map((filter) => {
|
|
14
|
+
const entries = Object.entries(filter);
|
|
15
|
+
const key = entries[0][0];
|
|
16
|
+
const value = entries[0][1];
|
|
17
|
+
const options = entries
|
|
18
|
+
.slice(1)
|
|
19
|
+
.reduce((opts, [k, v]) => ({ ...opts, [k]: v }), {});
|
|
20
|
+
|
|
21
|
+
return { key, value, options };
|
|
22
|
+
})
|
|
23
|
+
: // old format
|
|
24
|
+
Object.entries(rawFilters).map(([key, value]) => ({
|
|
25
|
+
key,
|
|
26
|
+
value,
|
|
27
|
+
}));
|
|
28
|
+
|
|
29
|
+
return filters.map(({ key, value, options = {} }) => {
|
|
30
|
+
const fn = this[key];
|
|
11
31
|
|
|
12
32
|
return typeof fn === "function"
|
|
13
|
-
? (records) => fn.call(this, records, value)
|
|
33
|
+
? (records) => fn.call(this, records, value, options)
|
|
14
34
|
: (records) => records;
|
|
15
35
|
});
|
|
16
36
|
}
|
|
17
37
|
|
|
38
|
+
sort(records, order) {
|
|
39
|
+
if (!order) return records;
|
|
40
|
+
|
|
41
|
+
return records.sort((a, b) => {
|
|
42
|
+
return (
|
|
43
|
+
order
|
|
44
|
+
.map((o) => {
|
|
45
|
+
const attr = camelize(o.attribute.toLowerCase());
|
|
46
|
+
const direction = o.direction === "ASC" ? -1 : 1;
|
|
47
|
+
|
|
48
|
+
return (b[attr] - a[attr]) * direction;
|
|
49
|
+
})
|
|
50
|
+
.find((result) => result !== 0) ?? 0
|
|
51
|
+
);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
18
55
|
filter(records, filters) {
|
|
19
|
-
|
|
20
|
-
const filterObj = Array.isArray(filters)
|
|
21
|
-
? filters.length
|
|
22
|
-
? Object.assign(...filters)
|
|
23
|
-
: {}
|
|
24
|
-
: filters;
|
|
25
|
-
|
|
26
|
-
return this._getFilterFns(filterObj).reduce(
|
|
56
|
+
return this._getFilterFns(filters.filter ?? filters).reduce(
|
|
27
57
|
(recs, fn) => fn(recs),
|
|
28
|
-
records
|
|
58
|
+
this.sort(records, filters?.order)
|
|
29
59
|
);
|
|
30
60
|
}
|
|
31
61
|
|
|
32
62
|
find(records, filters) {
|
|
33
|
-
return (
|
|
34
|
-
this._getFilterFns(filters).reduce((recs, fn) => fn(recs), records)[0] ||
|
|
35
|
-
null
|
|
36
|
-
);
|
|
63
|
+
return this.filter(records, filters)[0] || null;
|
|
37
64
|
}
|
|
38
65
|
|
|
39
66
|
slug(records, value) {
|
|
@@ -12,10 +12,8 @@ export default class extends BaseFilter {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
excludeForms(records, value) {
|
|
15
|
-
const forms = this.db.forms.filter(({ slug }) => value.includes(slug));
|
|
16
|
-
|
|
17
15
|
return records.filter(
|
|
18
|
-
({ formIds }) => !
|
|
16
|
+
({ formIds }) => !value.some((id) => (formIds || []).includes(id))
|
|
19
17
|
);
|
|
20
18
|
}
|
|
21
19
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import BaseFilter from "@projectcaluma/ember-testing/mirage-graphql/filters/base";
|
|
2
|
+
|
|
3
|
+
export default class extends BaseFilter {
|
|
4
|
+
status(records, value, { invert = false }) {
|
|
5
|
+
return records.filter(({ status }) => invert !== (status === value));
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
tasks(records, value, { invert = false }) {
|
|
9
|
+
return records.filter((record) => invert !== value.includes(record.taskId));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
task(records, value, { invert = false }) {
|
|
13
|
+
return this.tasks(records, [value], { invert });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
controllingGroups(records, value, { invert = false }) {
|
|
17
|
+
return records.filter((record) =>
|
|
18
|
+
value.every((g) => invert !== record.controllingGroups?.includes(g))
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
addressedGroups(records, value, { invert = false }) {
|
|
23
|
+
return records.filter((record) =>
|
|
24
|
+
value.every((g) => invert !== record.addressedGroups?.includes(g))
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -1,25 +1,35 @@
|
|
|
1
1
|
import { classify } from "@ember/string";
|
|
2
2
|
import { singularize } from "ember-inflector";
|
|
3
3
|
import { graphql } from "graphql";
|
|
4
|
+
import {
|
|
5
|
+
GraphQLDate as Date,
|
|
6
|
+
GraphQLDateTime as DateTime,
|
|
7
|
+
} from "graphql-iso-date";
|
|
4
8
|
import { addMockFunctionsToSchema, makeExecutableSchema } from "graphql-tools";
|
|
5
|
-
import moment from "moment";
|
|
6
9
|
|
|
7
10
|
import { Mock } from "@projectcaluma/ember-testing/mirage-graphql";
|
|
8
|
-
import
|
|
9
|
-
import rawSchema from "@projectcaluma/ember-testing/mirage-graphql/schema.graphql";
|
|
11
|
+
import typeDefs from "@projectcaluma/ember-testing/mirage-graphql/schema.graphql";
|
|
10
12
|
|
|
11
13
|
export default function (server) {
|
|
12
14
|
return function ({ db }, request) {
|
|
13
15
|
const mocks = db._collections.reduce((m, { name }) => {
|
|
14
16
|
const cls = classify(singularize(name));
|
|
15
|
-
const mock = new Mock(cls,
|
|
17
|
+
const mock = new Mock(cls, server);
|
|
16
18
|
|
|
17
19
|
return { ...m, ...mock.getHandlers() };
|
|
18
20
|
}, {});
|
|
19
21
|
|
|
20
22
|
const schema = makeExecutableSchema({
|
|
21
|
-
typeDefs
|
|
22
|
-
resolvers
|
|
23
|
+
typeDefs,
|
|
24
|
+
resolvers: {
|
|
25
|
+
Date,
|
|
26
|
+
DateTime,
|
|
27
|
+
GenericScalar: {
|
|
28
|
+
serialize(value) {
|
|
29
|
+
return typeof value === "string" ? JSON.parse(value) : value;
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
23
33
|
resolverValidationOptions: { requireResolversForResolveType: false },
|
|
24
34
|
});
|
|
25
35
|
|
|
@@ -29,10 +39,18 @@ export default function (server) {
|
|
|
29
39
|
schema,
|
|
30
40
|
mocks: {
|
|
31
41
|
...mocks,
|
|
32
|
-
Date: () => moment().format(moment.HTML5_FMT.DATE),
|
|
33
42
|
JSONString: () => JSON.stringify({}),
|
|
34
43
|
GenericScalar: () => ({}),
|
|
35
44
|
Node: (_, { id }) => ({ __typename: atob(id).split(":")[0] }),
|
|
45
|
+
SelectedOption: ({ value }) => {
|
|
46
|
+
const option = server.schema.options.findBy({ slug: value });
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
slug: value,
|
|
50
|
+
label: option.label,
|
|
51
|
+
__typename: "SelectedOption",
|
|
52
|
+
};
|
|
53
|
+
},
|
|
36
54
|
},
|
|
37
55
|
preserveResolvers: false,
|
|
38
56
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { dasherize } from "@ember/string";
|
|
1
|
+
import { dasherize, classify } from "@ember/string";
|
|
2
2
|
import require from "require";
|
|
3
3
|
|
|
4
4
|
const importTypeOrBase = (path, type) => {
|
|
@@ -28,8 +28,29 @@ export const register = (tpl) => (target, name, descriptor) => {
|
|
|
28
28
|
return descriptor;
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
-
export const
|
|
32
|
-
|
|
31
|
+
export const serialize = (deserialized = {}, type) => {
|
|
32
|
+
const __typename = [deserialized.type?.toLowerCase(), type]
|
|
33
|
+
.filter(Boolean)
|
|
34
|
+
.map(classify)
|
|
35
|
+
.join("");
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
...deserialized,
|
|
39
|
+
id: btoa(`${__typename}:${deserialized.id}`),
|
|
40
|
+
__typename,
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const deserialize = (serialized) => {
|
|
45
|
+
let decodedId = serialized.id;
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
decodedId = atob(serialized.id).split(":")[1] || serialized.id;
|
|
49
|
+
} catch (e) {
|
|
50
|
+
// this is expected most times
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return { ...serialized, ...(decodedId ? { id: decodedId } : {}) };
|
|
33
54
|
};
|
|
34
55
|
|
|
35
56
|
export const Filter = function (type, ...args) {
|
|
@@ -4,46 +4,21 @@ import { register } from "@projectcaluma/ember-testing/mirage-graphql";
|
|
|
4
4
|
import BaseMock from "@projectcaluma/ember-testing/mirage-graphql/mocks/base";
|
|
5
5
|
|
|
6
6
|
export default class extends BaseMock {
|
|
7
|
-
@register("Answer")
|
|
8
|
-
handleAnswer({ __typename }) {
|
|
9
|
-
return { __typename };
|
|
10
|
-
}
|
|
11
|
-
|
|
12
7
|
_handleSaveDocumentAnswer(
|
|
13
8
|
_,
|
|
14
|
-
{
|
|
15
|
-
question: questionSlug,
|
|
16
|
-
document: documentId,
|
|
17
|
-
clientMutationId,
|
|
18
|
-
value,
|
|
19
|
-
type,
|
|
20
|
-
}
|
|
9
|
+
{ question: questionId, document: documentId, value, type }
|
|
21
10
|
) {
|
|
22
|
-
const questionId = this.db.questions.findBy({ slug: questionSlug }).id;
|
|
23
|
-
|
|
24
11
|
const answer = this.collection.findBy({ questionId, documentId });
|
|
25
12
|
|
|
26
|
-
|
|
13
|
+
return this.handleSavePayload.fn.call(this, _, {
|
|
27
14
|
input: {
|
|
28
|
-
id: answer
|
|
15
|
+
id: answer?.id,
|
|
29
16
|
type,
|
|
30
17
|
value,
|
|
31
18
|
documentId,
|
|
32
19
|
questionId,
|
|
33
|
-
clientMutationId,
|
|
34
20
|
},
|
|
35
21
|
});
|
|
36
|
-
|
|
37
|
-
// Default answers don't have a document.
|
|
38
|
-
if (res.answer.documentId) {
|
|
39
|
-
const doc = this.db.documents.findBy({ id: res.answer.documentId });
|
|
40
|
-
|
|
41
|
-
this.db.documents.update(doc.id, {
|
|
42
|
-
answerIds: [...new Set([...(doc.answerIds || []), res.answer.id])],
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return res;
|
|
47
22
|
}
|
|
48
23
|
|
|
49
24
|
@register("SaveDocumentStringAnswerPayload")
|
|
@@ -51,7 +26,7 @@ export default class extends BaseMock {
|
|
|
51
26
|
handleSaveDocumentStringAnswer(_, { input }) {
|
|
52
27
|
return this._handleSaveDocumentAnswer(_, {
|
|
53
28
|
...input,
|
|
54
|
-
value: String(input.value),
|
|
29
|
+
value: input.value ? String(input.value) : null,
|
|
55
30
|
type: "STRING",
|
|
56
31
|
});
|
|
57
32
|
}
|
|
@@ -61,7 +36,7 @@ export default class extends BaseMock {
|
|
|
61
36
|
handleSaveIntegerAnswer(_, { input }) {
|
|
62
37
|
return this._handleSaveDocumentAnswer(_, {
|
|
63
38
|
...input,
|
|
64
|
-
value: parseInt(input.value),
|
|
39
|
+
value: input.value ? parseInt(input.value) : null,
|
|
65
40
|
type: "INTEGER",
|
|
66
41
|
});
|
|
67
42
|
}
|
|
@@ -71,7 +46,7 @@ export default class extends BaseMock {
|
|
|
71
46
|
handleSaveFloatAnswer(_, { input }) {
|
|
72
47
|
return this._handleSaveDocumentAnswer(_, {
|
|
73
48
|
...input,
|
|
74
|
-
value: parseFloat(input.value),
|
|
49
|
+
value: input.value ? parseFloat(input.value) : null,
|
|
75
50
|
type: "FLOAT",
|
|
76
51
|
});
|
|
77
52
|
}
|
|
@@ -81,7 +56,7 @@ export default class extends BaseMock {
|
|
|
81
56
|
handleSaveListAnswer(_, { input }) {
|
|
82
57
|
return this._handleSaveDocumentAnswer(_, {
|
|
83
58
|
...input,
|
|
84
|
-
value: [...input.value].map(String),
|
|
59
|
+
value: input.value ? [...input.value].map(String) : null,
|
|
85
60
|
type: "LIST",
|
|
86
61
|
});
|
|
87
62
|
}
|
|
@@ -90,7 +65,7 @@ export default class extends BaseMock {
|
|
|
90
65
|
handleSaveFileAnswer(_, { input }) {
|
|
91
66
|
return this._handleSaveDocumentAnswer(_, {
|
|
92
67
|
...input,
|
|
93
|
-
value: { metadata: { object_name: input.value } },
|
|
68
|
+
value: input.value ? { metadata: { object_name: input.value } } : null,
|
|
94
69
|
type: "FILE",
|
|
95
70
|
});
|
|
96
71
|
}
|
|
@@ -1,24 +1,70 @@
|
|
|
1
|
-
import { camelize, dasherize } from "@ember/string";
|
|
1
|
+
import { camelize, dasherize, classify } from "@ember/string";
|
|
2
|
+
import { singularize, pluralize } from "ember-inflector";
|
|
3
|
+
import faker from "faker";
|
|
2
4
|
import { MockList } from "graphql-tools";
|
|
3
5
|
|
|
4
6
|
import {
|
|
5
7
|
Filter,
|
|
6
|
-
Serializer,
|
|
7
8
|
register,
|
|
9
|
+
serialize,
|
|
10
|
+
deserialize,
|
|
8
11
|
} from "@projectcaluma/ember-testing/mirage-graphql";
|
|
9
12
|
|
|
13
|
+
export const ANSWER_TYPES = [
|
|
14
|
+
"DATE",
|
|
15
|
+
"FILE",
|
|
16
|
+
"FLOAT",
|
|
17
|
+
"INTEGER",
|
|
18
|
+
"LIST",
|
|
19
|
+
"STRING",
|
|
20
|
+
"TABLE",
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
export const QUESTION_TYPES = [
|
|
24
|
+
"ACTION_BUTTON",
|
|
25
|
+
"CALCULATED_FLOAT",
|
|
26
|
+
"CHOICE",
|
|
27
|
+
"DATE",
|
|
28
|
+
"DYNAMIC_CHOICE",
|
|
29
|
+
"DYNAMIC_MULTIPLE_CHOICE",
|
|
30
|
+
"FILE",
|
|
31
|
+
"FLOAT",
|
|
32
|
+
"FORM",
|
|
33
|
+
"INTEGER",
|
|
34
|
+
"MULTIPLE_CHOICE",
|
|
35
|
+
"STATIC",
|
|
36
|
+
"TABLE",
|
|
37
|
+
"TEXT",
|
|
38
|
+
"TEXTAREA",
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
export const TASK_TYPES = [
|
|
42
|
+
"SIMPLE",
|
|
43
|
+
"COMPLETE_WORKFLOW_FORM",
|
|
44
|
+
"COMPLETE_TASK_FORM",
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
export const TYPE_MAPPING = {
|
|
48
|
+
Answer: ANSWER_TYPES,
|
|
49
|
+
Question: QUESTION_TYPES,
|
|
50
|
+
Task: TASK_TYPES,
|
|
51
|
+
};
|
|
52
|
+
|
|
10
53
|
export default class {
|
|
11
|
-
constructor(type,
|
|
54
|
+
constructor(type, server) {
|
|
12
55
|
this.type = type;
|
|
13
|
-
this.collection = collection;
|
|
14
|
-
this.db = db;
|
|
15
56
|
this.server = server;
|
|
57
|
+
this.schema = server.schema;
|
|
58
|
+
|
|
59
|
+
this.filter = new Filter(type);
|
|
60
|
+
}
|
|
16
61
|
|
|
17
|
-
|
|
18
|
-
this.
|
|
62
|
+
get collection() {
|
|
63
|
+
return this.schema[pluralize(camelize(this.type))];
|
|
19
64
|
}
|
|
20
65
|
|
|
21
66
|
getHandlers() {
|
|
67
|
+
const types = TYPE_MAPPING[this.type];
|
|
22
68
|
const handlers = (target) => {
|
|
23
69
|
const proto = Reflect.getPrototypeOf(target);
|
|
24
70
|
const res = Object.values(proto);
|
|
@@ -37,10 +83,26 @@ export default class {
|
|
|
37
83
|
...handlers,
|
|
38
84
|
// Mocks can have multiple handlers per type.
|
|
39
85
|
...handler.__handlerFor.reduce((targets, target) => {
|
|
86
|
+
const handlerName = target.replace(/\{type\}/, this.type);
|
|
87
|
+
const baseHandlerName = handlerName.replace(/\{subtype\}/, "");
|
|
88
|
+
const fn = (...args) => handler.fn.apply(this, args);
|
|
89
|
+
|
|
90
|
+
const newHandlers = types
|
|
91
|
+
? types.reduce(
|
|
92
|
+
(typeHandlers, type) => ({
|
|
93
|
+
...typeHandlers,
|
|
94
|
+
[handlerName.replace(
|
|
95
|
+
/\{subtype\}/,
|
|
96
|
+
classify(type.toLowerCase())
|
|
97
|
+
)]: fn,
|
|
98
|
+
}),
|
|
99
|
+
{}
|
|
100
|
+
)
|
|
101
|
+
: { [baseHandlerName]: fn };
|
|
102
|
+
|
|
40
103
|
return {
|
|
41
104
|
...targets,
|
|
42
|
-
|
|
43
|
-
handler.fn.apply(this, args),
|
|
105
|
+
...newHandlers,
|
|
44
106
|
};
|
|
45
107
|
}, {}),
|
|
46
108
|
};
|
|
@@ -51,24 +113,27 @@ export default class {
|
|
|
51
113
|
}
|
|
52
114
|
|
|
53
115
|
@register("{type}Connection")
|
|
54
|
-
handleConnection(root, vars) {
|
|
116
|
+
handleConnection(root, vars, _, { fieldName }) {
|
|
55
117
|
let records = this.filter.filter(
|
|
56
|
-
this.collection,
|
|
57
|
-
|
|
118
|
+
this.collection.all().models,
|
|
119
|
+
deserialize(vars)
|
|
58
120
|
);
|
|
59
121
|
|
|
60
|
-
const relKey = `${
|
|
122
|
+
const relKey = `${singularize(fieldName)}Ids`;
|
|
61
123
|
if (root && Object.prototype.hasOwnProperty.call(root, relKey)) {
|
|
62
124
|
const ids = root[relKey];
|
|
63
|
-
records = records
|
|
125
|
+
records = records
|
|
126
|
+
.filter(({ id }) => ids && ids.includes(id))
|
|
127
|
+
.sort((a, b) => ids.indexOf(a.id) - ids.indexOf(b.id));
|
|
64
128
|
}
|
|
65
129
|
|
|
66
130
|
// add base64 encoded index as cursor to records
|
|
67
131
|
records = records.map((record, index) => ({
|
|
68
|
-
...record,
|
|
132
|
+
...record.toJSON(),
|
|
69
133
|
_cursor: btoa(index),
|
|
70
134
|
}));
|
|
71
135
|
|
|
136
|
+
const totalCount = records.length;
|
|
72
137
|
const lastCursor = records.slice(-1)[0]?._cursor;
|
|
73
138
|
|
|
74
139
|
// extract next page of records
|
|
@@ -86,71 +151,78 @@ export default class {
|
|
|
86
151
|
pageInfo: () => {
|
|
87
152
|
return { hasNextPage, endCursor };
|
|
88
153
|
},
|
|
154
|
+
totalCount,
|
|
89
155
|
edges: () =>
|
|
90
156
|
new MockList(records.length, () => ({
|
|
91
157
|
node: (r, v, _, meta) =>
|
|
92
|
-
|
|
158
|
+
serialize(records[meta.path.prev.key], this.type),
|
|
93
159
|
})),
|
|
94
160
|
};
|
|
95
161
|
}
|
|
96
162
|
|
|
97
|
-
@register("{type}")
|
|
98
|
-
handle(root, vars) {
|
|
163
|
+
@register("{subtype}{type}")
|
|
164
|
+
handle(root, vars, _, { fieldName }) {
|
|
99
165
|
// If the parent node already resolved this branch in the graph, return it
|
|
100
166
|
// directly without mocking it
|
|
101
167
|
if (
|
|
102
168
|
root &&
|
|
103
|
-
|
|
169
|
+
fieldName !== "node" &&
|
|
170
|
+
Object.prototype.hasOwnProperty.call(root, fieldName)
|
|
104
171
|
) {
|
|
105
|
-
return root[
|
|
172
|
+
return root[fieldName];
|
|
106
173
|
}
|
|
107
174
|
|
|
108
175
|
// If the parent node provides an ID for this relation, filter our mock data
|
|
109
176
|
// with that given ID
|
|
177
|
+
const relKey = `${fieldName}Id`;
|
|
110
178
|
if (
|
|
111
179
|
root &&
|
|
112
|
-
|
|
180
|
+
fieldName !== "node" &&
|
|
181
|
+
Object.prototype.hasOwnProperty.call(root, relKey)
|
|
113
182
|
) {
|
|
114
|
-
vars = { id: root[
|
|
183
|
+
vars = { id: root[relKey] };
|
|
115
184
|
}
|
|
116
185
|
|
|
117
|
-
const record = this.
|
|
118
|
-
this.collection,
|
|
119
|
-
this.serializer.deserialize(vars)
|
|
120
|
-
);
|
|
121
|
-
|
|
122
|
-
/* istanbul ignore next */
|
|
123
|
-
if (!record) {
|
|
124
|
-
// eslint-disable-next-line no-console
|
|
125
|
-
return Error(
|
|
126
|
-
`Did not find a record of type "${this.type}" in the store. Did you forget to create one?`
|
|
127
|
-
);
|
|
128
|
-
}
|
|
186
|
+
const record = this.collection.findBy(deserialize(vars));
|
|
129
187
|
|
|
130
|
-
return
|
|
188
|
+
return record && serialize(record.toJSON(), this.type);
|
|
131
189
|
}
|
|
132
190
|
|
|
133
|
-
@register("Save{type}Payload")
|
|
134
|
-
handleSavePayload(
|
|
191
|
+
@register("Save{subtype}{type}Payload")
|
|
192
|
+
handleSavePayload(
|
|
193
|
+
_,
|
|
194
|
+
{ input: { clientMutationId = faker.datatype.uuid(), slug, id, ...args } }
|
|
195
|
+
) {
|
|
135
196
|
const identifier = slug ? { slug } : { id };
|
|
136
197
|
|
|
137
|
-
const
|
|
198
|
+
const relKeys = this.schema.modelFor(camelize(this.type)).foreignKeys;
|
|
199
|
+
|
|
200
|
+
const parsedArgs = Object.entries(args).reduce((parsed, [key, value]) => {
|
|
201
|
+
const re = new RegExp(`${camelize(key)}Id(s)?`);
|
|
202
|
+
const relKey = relKeys.find((k) => re.test(k));
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
...parsed,
|
|
206
|
+
...(value === undefined ? {} : { [relKey ?? key]: value }),
|
|
207
|
+
};
|
|
208
|
+
}, {});
|
|
209
|
+
|
|
210
|
+
const obj = this.collection.findBy(identifier);
|
|
138
211
|
const res = obj
|
|
139
|
-
?
|
|
140
|
-
: this.collection.
|
|
141
|
-
this.
|
|
142
|
-
|
|
212
|
+
? obj.update(deserialize(parsedArgs))
|
|
213
|
+
: this.collection.create(
|
|
214
|
+
this.server.build(
|
|
215
|
+
dasherize(this.type),
|
|
216
|
+
deserialize({
|
|
143
217
|
...identifier,
|
|
144
|
-
...
|
|
218
|
+
...parsedArgs,
|
|
145
219
|
})
|
|
146
220
|
)
|
|
147
221
|
);
|
|
148
222
|
|
|
149
|
-
|
|
150
|
-
[camelize(this.type)]:
|
|
223
|
+
return {
|
|
224
|
+
[camelize(this.type)]: serialize(res.toJSON(), this.type),
|
|
151
225
|
clientMutationId,
|
|
152
226
|
};
|
|
153
|
-
|
|
154
|
-
return x;
|
|
155
227
|
}
|
|
156
228
|
}
|