Skip to content

Commit 9ed5e01

Browse files
committed
add error messages, prevent creating record with existing id, use referenceField with filtering
1 parent 20158df commit 9ed5e01

File tree

23 files changed

+229
-182
lines changed

23 files changed

+229
-182
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
name: Adam
3+
friend: Amanda
4+
enjoys:
5+
- cats
6+
- coding
7+
- flatbread
8+
---
9+
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
name: Amanda
3+
enjoys:
4+
- cats
5+
- coding
6+
- flatbread
7+
---
8+

examples/content/markdown/authors/daes.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ enjoys:
66
- cats
77
- coffee
88
- design
9-
friend: 40s3
9+
friend: Eva
1010
date_joined: 2021-04-22T16:41:59.558Z
1111
skills:
1212
sitting: 304

examples/content/markdown/authors/eva.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ enjoys:
88
- mow mow
99
- sleepy time
1010
- attention
11-
friend: 2a3e
11+
friend: Tony
1212
image: eva.svg
1313
date_joined: 2002-02-25T16:41:59.558Z
1414
skills:

examples/content/markdown/authors/tony.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ enjoys:
66
- cats
77
- tea
88
- making this
9-
friend: 40s3
9+
friend: Eva
1010
image: tony.svg
1111
date_joined: 2021-02-25T16:41:59.558Z
1212
skills:

examples/content/markdown/authors/yoshi.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ enjoys:
77
- encroaching upon personal space
88
- being concerned
99
- smooth jazz
10-
friend: ab2c
10+
friend: Daes
1111
date_joined: 2018-10-25T16:23:59.558Z
1212
skills:
1313
sitting: 10

examples/content/markdown/posts/anotha-one.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
id: 92348fds-453fdh-59ddsd-3332-09876
33
title: 'Test post A'
44
authors:
5-
- 40s3
6-
- 2a3e
5+
- Eva
6+
- Tony
77
rating: 84.3
88
---
99

examples/content/markdown/posts/b.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
id: 2348fds-563fdh-59ddsd-3332-09876
33
title: 'Test post B'
44
authors:
5-
- 1111
6-
- ab2c
5+
- Ushi
6+
- Daes
77
rating: 44
88
---
99

examples/content/markdown/posts/example-post.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
id: sdfsdf-23423-sdfsd-23444-dfghf
33
title: 'Example post of things'
44
authors:
5-
- 2a3e
6-
- 40s3
5+
- Tony
6+
- Eva
77
rating: 74
88
---
99

examples/content/markdown/posts/soup.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22
id: jksfd4-234fdh-5345fj-3455-09836
33
title: 'Soup Reflection'
44
authors:
5-
- r3c6
6-
- ab2c
5+
- Yoshi
76
rating: 96
87
---
98

examples/sveltekit/flatbread.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export default defineConfig({
3434
{
3535
path: 'content/markdown/authors',
3636
name: 'Author',
37+
referenceField: 'name',
3738
refs: {
3839
friend: 'Author',
3940
},

packages/core/src/errors.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ import { outdent } from './utils/outdent';
22

33
export class IllegalFieldNameError extends Error {
44
constructor(illegalSequence: string) {
5-
super();
6-
this.message = outdent`
5+
super(outdent`
76
The sequence "${illegalSequence}" is reserved and not allowed in field names
87
Either:
98
- remove all instances of "${illegalSequence}" in the names of fields in your content
@@ -13,6 +12,22 @@ export class IllegalFieldNameError extends Error {
1312
...,
1413
fieldNameTransform: (value) => value.replaceAll("${illegalSequence}",'-')
1514
}
16-
`;
15+
`);
1716
}
1817
}
18+
19+
export class ReferenceAlreadyExistsError<K extends Record<string, K>> extends Error {
20+
constructor(
21+
payload: K,
22+
collectionName: string,
23+
metadata: { referenceField: string; reference: string }
24+
) {
25+
const payloadString = JSON.stringify(payload, null, 2);
26+
super(
27+
outdent`
28+
Failed to create
29+
${payloadString}
30+
${collectionName} with ${metadata.referenceField} of ${metadata.reference} already exists`
31+
);
32+
}
33+
}

packages/core/src/generators/arguments.ts

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export const generateArgsForAllItemQuery = (pluralType: string) => ({
1717
* @param pluralType plural name of the content type
1818
*/
1919
export const generateArgsForManyItemQuery = (pluralType: string) => ({
20-
ids: {
20+
references: {
2121
type: '[String]',
2222
},
2323
...skip(),
@@ -26,16 +26,6 @@ export const generateArgsForManyItemQuery = (pluralType: string) => ({
2626
...sortBy(pluralType),
2727
});
2828

29-
/**
30-
* Generates the accepted arguments for a 'single-item' query on a content type.
31-
*
32-
*/
33-
export const generateArgsForSingleItemQuery = () => ({
34-
id: {
35-
type: 'String',
36-
},
37-
});
38-
3929
/**
4030
* Argument for skipping the first `n` items from the query results.
4131
*/

packages/core/src/generators/collectionMutations.ts

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { ObjectTypeComposer, SchemaComposer } from 'graphql-compose';
22
import { get, merge } from 'lodash-es';
3+
import { ReferenceAlreadyExistsError } from '../errors';
34
import {
45
CollectionContext,
56
CollectionEntry,
@@ -17,17 +18,15 @@ export interface AddCollectionMutationsArgs {
1718
collectionEntry: LoadedCollectionEntry;
1819
updateCollectionRecord: (
1920
collection: CollectionEntry,
20-
entry: EntryNode & { _metadata: CollectionContext }
21+
entry: EntryNode & { _metadata: Partial<CollectionContext> }
2122
) => Promise<EntryNode>;
2223
}
2324

2425
export default function addCollectionMutations(
2526
args: AddCollectionMutationsArgs
26-
) {
27+
): void {
2728
const {
2829
name,
29-
pluralName,
30-
config,
3130
objectComposer,
3231
schemaComposer,
3332
updateCollectionRecord,
@@ -41,27 +40,42 @@ export default function addCollectionMutations(
4140
// remove _metadata to prevent injection
4241
const { _metadata, ...update } = payload?.[name];
4342

44-
const targetRecord = objectComposer
45-
.getResolver('findById')
46-
.resolve({ args: update });
47-
4843
// remove supplied key (might not be required)
49-
delete update[targetRecord._metadata.referenceField];
50-
const newRecord = merge(targetRecord, update);
44+
delete update[existing._metadata.referenceField];
45+
const newRecord = merge(existing, update);
5146

52-
await updateCollectionRecord(collectionEntry, newRecord);
47+
await updateCollectionRecord(
48+
collectionEntry,
49+
newRecord as EntryNode & { _metadata: Partial<CollectionContext> }
50+
);
5351

5452
return newRecord;
5553
}
5654

5755
async function create(source: unknown, payload: Record<string, EntryNode>) {
56+
const existingRecordWithId = await objectComposer
57+
.getResolver('findByReferenceField')
58+
.resolve({ args: payload[name] });
59+
60+
if (existingRecordWithId) {
61+
throw new ReferenceAlreadyExistsError(
62+
payload[name],
63+
name,
64+
existingRecordWithId._metadata
65+
);
66+
}
67+
5868
collectionEntry.creationRequiredFields.forEach((field) => {
59-
if (!payload[name].hasOwnProperty(field))
69+
// const
70+
71+
if (Object.hasOwn(payload[name], field))
6072
throw new Error(
6173
`field ${field} is required when creating a new ${name}`
6274
);
6375
});
6476

77+
console.log({ collectionEntry });
78+
6579
const record = merge(payload[name], {
6680
_metadata: {
6781
referenceField: collectionEntry.referenceField ?? 'id',
@@ -87,7 +101,7 @@ export default function addCollectionMutations(
87101
const { _metadata, ...args } = payload?.[name];
88102

89103
const existingRecord = objectComposer
90-
.getResolver('findById')
104+
.getResolver('findByReferenceField')
91105
.resolve({ args });
92106

93107
if (!existingRecord)
@@ -106,8 +120,6 @@ export default function addCollectionMutations(
106120
args: {
107121
[name]: objectComposer
108122
.getInputTypeComposer()
109-
.clone(`${name}CreateInput`)
110-
.removeField('id')
111123
.makeFieldNonNull(collectionEntry.creationRequiredFields),
112124
},
113125
description: `Create a ${name}`,
@@ -120,7 +132,7 @@ export default function addCollectionMutations(
120132
const { _metadata, ...args } = payload?.[name];
121133

122134
const existingRecord = objectComposer
123-
.getResolver('findById')
135+
.getResolver('findByReferenceField')
124136
.resolve({ args });
125137

126138
if (existingRecord) return update(payload, existingRecord);

packages/core/src/generators/collectionQueries.ts

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import { ObjectTypeComposer, SchemaComposer } from 'graphql-compose';
22
import resolveQueryArgs from '../resolvers/arguments';
33

4+
import { cloneDeep, get } from 'lodash-es';
45
import {
56
generateArgsForAllItemQuery,
67
generateArgsForManyItemQuery,
7-
generateArgsForSingleItemQuery,
88
} from '../generators/arguments';
9-
import { cloneDeep } from 'lodash-es';
10-
import { EntryNode, LoadedFlatbreadConfig, Transformer } from '../types';
9+
import {
10+
EntryNode,
11+
LoadedCollectionEntry,
12+
LoadedFlatbreadConfig,
13+
Transformer,
14+
} from '../types';
1115

1216
export interface AddCollectionQueriesArgs {
1317
name: string;
@@ -17,6 +21,7 @@ export interface AddCollectionQueriesArgs {
1721
schemaComposer: SchemaComposer;
1822
allContentNodesJSON: Record<string, any[]>;
1923
transformersById: Record<string, Transformer>;
24+
collectionEntry: LoadedCollectionEntry;
2025
}
2126

2227
export default function addCollectionQueries(args: AddCollectionQueriesArgs) {
@@ -26,36 +31,44 @@ export default function addCollectionQueries(args: AddCollectionQueriesArgs) {
2631
config,
2732
objectComposer,
2833
schemaComposer,
34+
collectionEntry,
2935
allContentNodesJSON,
3036
} = args;
3137

3238
const pluralTypeQueryName = 'all' + pluralName;
3339

3440
objectComposer.addResolver({
35-
name: 'findById',
41+
name: 'findByReferenceField',
3642
type: () => objectComposer,
37-
description: `Find one ${name} by its ID`,
38-
args: generateArgsForSingleItemQuery(),
43+
description: `Find one ${name} by its ${collectionEntry.referenceField}`,
44+
args: {
45+
[collectionEntry.referenceField]: objectComposer
46+
.getInputTypeComposer()
47+
.getField(collectionEntry.referenceField),
48+
},
3949
resolve: (rp: Record<string, any>) =>
4050
cloneDeep(
4151
allContentNodesJSON[name].find(
42-
(node: EntryNode) => node.id === rp.args.id
52+
(node: EntryNode) =>
53+
node[collectionEntry.referenceField] ===
54+
rp.args[collectionEntry.referenceField]
4355
)
4456
),
4557
});
4658

4759
objectComposer.addResolver({
4860
name: 'findMany',
4961
type: () => [objectComposer],
50-
description: `Find many ${pluralName} by their IDs`,
62+
description: `Find many ${pluralName} by their ${collectionEntry.referenceField}`,
5163
args: generateArgsForManyItemQuery(pluralName),
5264
resolve: (rp: Record<string, any>) => {
53-
const idsToFind = rp.args.ids ?? [];
65+
const referencesToFind = rp.args.references ?? [];
5466
const matches =
5567
cloneDeep(allContentNodesJSON[name])?.filter((node: EntryNode) =>
56-
idsToFind?.includes(node.id)
68+
referencesToFind?.includes(get(node, collectionEntry.referenceField))
5769
) ?? [];
5870
return resolveQueryArgs(matches, rp.args, config, {
71+
collectionEntry,
5972
type: {
6073
name: name,
6174
pluralName: pluralName,
@@ -73,6 +86,7 @@ export default function addCollectionQueries(args: AddCollectionQueriesArgs) {
7386
resolve: (rp: Record<string, any>) => {
7487
const nodes = cloneDeep(allContentNodesJSON[name]);
7588
return resolveQueryArgs(nodes, rp.args, config, {
89+
collectionEntry,
7690
type: {
7791
name: name,
7892
pluralName: pluralName,
@@ -86,7 +100,7 @@ export default function addCollectionQueries(args: AddCollectionQueriesArgs) {
86100
/**
87101
* Add find by ID to each content type
88102
*/
89-
[name]: objectComposer.getResolver('findById'),
103+
[name]: objectComposer.getResolver('findByReferenceField'),
90104
/**
91105
* Add find 'many' to each content type
92106
*/

0 commit comments

Comments
 (0)