Skip to content

Commit

Permalink
disable star paths, add basic create mutations
Browse files Browse the repository at this point in the history
  • Loading branch information
odama626 committed Aug 7, 2022
1 parent 27912b9 commit a1fd32d
Show file tree
Hide file tree
Showing 11 changed files with 144 additions and 58 deletions.
1 change: 1 addition & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"lodash-es": "4.17.21",
"lru-cache": "7.13.2",
"matcher": "5.0.0",
"nanoid": "4.0.0",
"plur": "5.1.0"
},
"devDependencies": {
Expand Down
35 changes: 30 additions & 5 deletions packages/core/src/generators/collectionMutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { ObjectTypeComposer, SchemaComposer } from 'graphql-compose';
import { merge } from 'lodash-es';
import {
CollectionContext,
CollectionEntry,
EntryNode,
LoadedFlatbreadConfig,
Transformer,
} from '../types';

export interface AddCollectionMutationsArgs {
Expand All @@ -13,7 +13,9 @@ export interface AddCollectionMutationsArgs {
config: LoadedFlatbreadConfig;
objectComposer: ObjectTypeComposer;
schemaComposer: SchemaComposer;
collectionEntry: CollectionEntry;
updateCollectionRecord: (
collection: CollectionEntry,
entry: EntryNode & { _metadata: CollectionContext }
) => Promise<EntryNode>;
}
Expand All @@ -28,16 +30,17 @@ export default function addCollectionMutations(
objectComposer,
schemaComposer,
updateCollectionRecord,
collectionEntry,
} = args;

schemaComposer.Mutation.addFields({
[`update${name}`]: {
type: objectComposer,
args: { [name]: objectComposer.getInputType() },
description: `Update or create a ${name}`,
args: { [name]: objectComposer.getInputTypeComposer() },
description: `Update a ${name}`,
async resolve(source, payload) {
// remove _metadata to prevent injection
const { _metadata, ...update } = source.author;
const { _metadata, ...update } = payload[name];

const targetRecord = objectComposer
.getResolver('findById')
Expand All @@ -47,10 +50,32 @@ export default function addCollectionMutations(
delete update[targetRecord._metadata.referenceField];
const newRecord = merge(targetRecord, update);

await updateCollectionRecord(newRecord);
await updateCollectionRecord(collectionEntry, newRecord);

return newRecord;
},
},
[`create${name}`]: {
type: objectComposer,
args: {
[name]: objectComposer
.getInputTypeComposer()
.clone(`${name}CreateInput`)
.removeField('id'),
},
description: `Create a ${name}`,
async resolve(source, payload, args) {
const record = merge(payload[name], {
_metadata: {
referenceField: collectionEntry.referenceField ?? 'id',
collection: name,
transformedBy: collectionEntry?.defaultTransformer,
sourcedBy: collectionEntry?.defaultSource,
},
});

return await updateCollectionRecord(collectionEntry, record);
},
},
});
}
42 changes: 31 additions & 11 deletions packages/core/src/generators/schema.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { schemaComposer } from 'graphql-compose';
import { composeWithJson } from 'graphql-compose-json';
import { defaultsDeep, get, merge } from 'lodash-es';
import { defaultsDeep, get, merge, set } from 'lodash-es';
import { nanoid } from 'nanoid';
import plur from 'plur';
import { VFile } from 'vfile';

Expand All @@ -12,6 +13,7 @@ import {
LoadedFlatbreadConfig,
Source,
Transformer,
CollectionEntry,
} from '../types';
import { getFieldOverrides } from '../utils/fieldOverrides';
import { map } from '../utils/map';
Expand Down Expand Up @@ -108,18 +110,33 @@ export async function generateSchema(
undefined: config.transformer[0],
};

async function updateCollectionRecord(entry: EntryNode & { _metadata: any }) {
const { _metadata: ctx, ...record } = entry;
const { serialize } = transformersById[ctx.transformedBy];
async function updateCollectionRecord(
collection: CollectionEntry,
entry: EntryNode & { _metadata: any }
) {
const ctx = entry._metadata;
const { serialize, id: transformerId } =
transformersById[ctx.transformedBy];

if (ctx.reference) {
const index = allContentNodesJSON[ctx.collection].findIndex(
(c) => get(c, ctx.referenceField) === ctx.reference
);

if (index < 0) throw new Error('Failed to find record to update');
// replace in memory representation of record
allContentNodesJSON[ctx.collection][index] = entry;
} else {
entry._metadata.reference = nanoid();
set(entry, entry._metadata.referenceField, entry._metadata.reference);
entry._metadata.transformedBy = transformerId;
allContentNodesJSON[ctx.collection].push(entry);
}

const { _metadata, ...record } = entry;
const file = await serialize(record, ctx.transformContext);
await config?.source.put(file, ctx.sourceContext, ctx);

await config?.source.put(file, ctx.sourceContext);
const index = allContentNodesJSON[ctx.collection].findIndex(
(c) => get(c, ctx.referenceField) === ctx.reference
);

// replace in memory representation of record
allContentNodesJSON[ctx.collection][index] = entry;
return entry;
}

Expand All @@ -143,6 +160,8 @@ export async function generateSchema(
/// Query resolvers
//

// TODO: add a new type of plugin that can add resolvers to each collection, they should be called here

addCollectionQueries({
name,
pluralName,
Expand All @@ -160,6 +179,7 @@ export async function generateSchema(
schemaComposer,
updateCollectionRecord,
config,
collectionEntry: config.content.find((c) => c.name === name),
});
}

Expand Down
38 changes: 37 additions & 1 deletion packages/core/src/providers/test/base.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ test('update collection record', async (t) => {
const flatbread = basicProject();
const sitting = (Math.random() * 100) | 0;
const result: any = await flatbread.query({
rootValue: { author: { id: '2a3e', skills: { sitting } } },
variableValues: { author: { id: '2a3e', skills: { sitting } } },
source: `
mutation UpdateAuthor($author: AuthorInput){
updateAuthor(Author: $author) {
Expand Down Expand Up @@ -96,3 +96,39 @@ test('update collection record', async (t) => {

t.is(updated.data.Author.skills.sitting, sitting);
});

test('create collection record', async (t) => {
const flatbread = basicProject();
const sitting = 69;
const result: any = await flatbread.query({
variableValues: { test: { skills: { sitting } } },
source: `
mutation CreateAuthor($test: AuthorCreateInput){
createAuthor(Author: $test) {
id
skills {
sitting
}
}
}
`,
});

t.is(result.data.createAuthor.skills.sitting, sitting);

const updated: any = await flatbread.query({
variableValues: { id: result.data.createAuthor.id },
source: `
query QueryAuthor($id: String) {
Author(id: $id) {
id
skills {
sitting
}
}
}
`,
});

t.is(updated.data.Author.skills.sitting, sitting);
});
12 changes: 8 additions & 4 deletions packages/core/src/sources/virtual.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,16 @@ export class SourceVirtual implements Source<MemContext> {
}
}

async put(doc: VFile, context: MemContext) {
const record = this.data[context.collectionName].find(
(entry) => entry.path === context.id
async put(doc: VFile, context: MemContext, parentContext: any) {
const record = this.data[parentContext.collection].find(
(entry) => entry.path === parentContext.reference
);

record.value = doc;
if (record) {
record.value = doc.value;
} else {
this.data[parentContext.collection].push(doc);
}

return { doc, context };
}
Expand Down
5 changes: 4 additions & 1 deletion packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ export interface Source<Context> {
id?: string;
put: (
source: VFile,
ctx: Context
ctx: Context,
parentContext: any
) => Promise<{ doc: VFile; context: Context }>;
fetch: (
allContentTypes: LoadedCollectionEntry[],
Expand Down Expand Up @@ -133,6 +134,8 @@ export interface CollectionEntry {
overrides?: Override[];
refs?: Record<string, string>;
referenceField?: string;
defaultTransformer?: string;
defaultSource?: string;
}

export interface LoadedCollectionEntry extends CollectionEntry {
Expand Down
25 changes: 7 additions & 18 deletions packages/source-filesystem/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import slugify from '@sindresorhus/slugify';
import { defaultsDeep, merge } from 'lodash-es';
import { read, write } from 'to-vfile';
import ownPackage from '../package.json' assert { type: 'json' };
import type {
CollectionContext,
LoadedCollectionEntry,
FlatbreadArgs,
LoadedCollectionEntry,
LoadedFlatbreadConfig,
SourcePlugin,
} from '@flatbread/core';
import slugify from '@sindresorhus/slugify';
import { defaultsDeep } from 'lodash-es';
import { relative, resolve } from 'path';
import { read, write } from 'to-vfile';
import type { VFile } from 'vfile';
import type {
FileNode,
Expand Down Expand Up @@ -65,7 +62,7 @@ async function getAllNodes(
flatbread: FlatbreadArgs<Context>,
config: InitializedSourceFilesystemConfig
): Promise<void> {
const nodeEntries = await Promise.all(
await Promise.all(
allCollectionEntries.map(
async (contentType): Promise<Record<string, any>> =>
new Promise(async (res) =>
Expand All @@ -76,18 +73,10 @@ async function getAllNodes(
)
)
);

const nodes = Object.fromEntries(
nodeEntries as Iterable<readonly [PropertyKey, any]>
);
}

// TODO: _flatbread data should be extracted from plugins
// plugin should return a context object and be given the same context object back when saving,
// this context object will be saved internally under _flatbread[collectionId]

async function put(doc: VFile, context: Context) {
doc.basename = context.filename;
async function put(doc: VFile, context: Context, parentContext: any) {
doc.basename = context?.filename ?? parentContext.reference;
doc.path = resolve(process.cwd(), context.path);

await write(doc);
Expand Down
2 changes: 1 addition & 1 deletion packages/source-filesystem/src/utils/gatherFileNodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export default async function gatherFileNodes(
) ?? ['.md', '.mdx', '.markdown'];

// gather all the globs in the path ( [capture-groups], **, *)
const [pathPrefix, ...globs] = path.split(/\/(?:\[|\*+)/);
const [pathPrefix, ...globs] = path.split(/\/(?:\[)/);

// for each segment - gather names for capture groups
// and calculate what to remove from matches ex: [name].md => remove .md from match
Expand Down
32 changes: 16 additions & 16 deletions packages/source-filesystem/src/utils/tests/gatherFileNodes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,20 @@ test('basic case', async (t) => {
t.snapshot(result2);
});

test('double level recursion', async (t) => {
const result = await gatherFileNodes('deeply/**/*.md', opts);
t.snapshot(result);
});
// test('double level recursion', async (t) => {
// const result = await gatherFileNodes('deeply/**/*.md', opts);
// t.snapshot(result);
// });

test('double level recursion named', async (t) => {
const result = await gatherFileNodes('deeply/[a]/[b].md', opts);
t.snapshot(result);
});

test('single level recursion', async (t) => {
const result = await gatherFileNodes('./*.md', opts as any);
t.snapshot(result);
});
// test('single level recursion', async (t) => {
// const result = await gatherFileNodes('./*.md', opts as any);
// t.snapshot(result);
// });

test('double level recursion named without parent directory', async (t) => {
const result = await gatherFileNodes('./[genre]/[title].md', opts);
Expand All @@ -57,15 +57,15 @@ test('single level named', async (t) => {
t.snapshot(result);
});

test('double level first named', async (t) => {
const result = await gatherFileNodes('./[genre]/*.md', opts);
t.snapshot(result);
});
// test('double level first named', async (t) => {
// const result = await gatherFileNodes('./[genre]/*.md', opts);
// t.snapshot(result);
// });

test('double level second named', async (t) => {
const result = await gatherFileNodes('./**/[title].md', opts);
t.snapshot(result);
});
// test('double level second named', async (t) => {
// const result = await gatherFileNodes('./**/[title].md', opts);
// t.snapshot(result);
// });

test('triple level', async (t) => {
const result = await gatherFileNodes('./[random]/[name]/[title].md', opts);
Expand Down
2 changes: 1 addition & 1 deletion packages/transformer-markdown/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ function serialize(
config: MarkdownTransformerConfig
) {
const { _content, ...rest } = data;
const doc = matter.stringify(_content.raw, rest, config.grayMatter);
const doc = matter.stringify(_content?.raw ?? '', rest, config.grayMatter);

return new VFile(doc);
}
Expand Down
Loading

0 comments on commit a1fd32d

Please sign in to comment.