// Simple data type to represent a query path and corresponding schema path tied together, because these are basically always used together, and it would bloat up the implementation code otherwise
Whensetting`$recurse: true`onachildproperty,theparentschemagetsduplicatedwiththechildschemamergedintoit,andtheresultingcombinedschemaisusedfortherecursivefetching.Becausethechildschemacanexplicitlysetpropertiestofalse,thisallowsforboth"fetch in parent but not in recursed children"cases(trueinparent,falseinchild)and"fetch in recursed children but not in parent"cases(unspecifiedorfalseinparent,trueinchild).
// TODO: Maybe clean up all the .child stuff here by moving the `$key` logic into the cursor implementation instead, as it seems like nothing else in dlayer needs to care about the aliasing
return{
...analyzeSubquery(childCursor.query),
schemaKey:schemaKey,
cursor:childCursor,
handler:handler
};
}
@ -101,87 +117,104 @@ function assignErrorPath(error, cursor) {
}
}
// MARKER: build a sample todo-list schema for testing out fetches, mutations, and combinations of them, including on collections
// When constructing the result object, we only care about the 'real' keys, not about special meta-keys like $key; those get processed in the actual resolution logic itself.
// NOTE: We're adding `i` to the query path for user feedback purposes, but we're not *actually* diving down into that property on the query object; the queryOverride doesn't just handle recursion, it also ensures that the 'original' subquery is passed in regardless of what the path suggests
// TODO: schemaOverride here is used to pass in the (asynchronously/lazily) resolved result, which the cursor implementation wouldn't have access to otherwise; need to somehow make it clearer in the API design that the automatic 'schema navigation' is only used for simple objects - maybe not call it an 'override' but instead just something like newSchema and newQuery?
letsubCursor=(i!=null)
?cursor
.child(queryKey,schemaKey)
.child(i,i,{
queryOverride:effectiveSubquery,
schemaOverride:item
})
:cursor
.child(queryKey,schemaKey,{
queryOverride:effectiveSubquery,
schemaOverride:item
});
returnevaluate(subCursor,context);
});
}else{
// null / undefined are returned as-is, so are leaves
returnvalue;
}
}).then((evaluated)=>{
// FIXME: Verify that this is still necessary here
if(allowErrors){
returnResult.ok(evaluated);
}else{
returnevaluated;
}
});
}else{
leterror=result.error();
if(error.__dlayerAcceptableError===true){
if(allowErrors===true){
returnResult.error(error.inner);
}else{
throwerror.inner;
}
// TODO: build a sample todo-list schema for testing out fetches, mutations, and combinations of them, including on collections
functionmakeEnvironment(context){
functioncallHandler(instruction){
// NOTE: cursor is assumed to already be the key of the child
// When constructing the result object, we only care about the 'real' keys, not about special meta-keys like $key; those get processed in the actual resolution logic itself.
returnmapObject.mapObjectSkip;
}else{
thrownewError(`No key '${schemaKey}' exists in the schema`);
// FIXME: This is hacky, and should be made more ergonomic...
// FIXME: We're absorbing Result.errors here, but that's a bit weird. We should probably be consistently carrying Result values throughout the implementation, and only unwrap them at the last moment?
return(instruction.allowErrors)
?Result.ok(finalValue)
:finalValue;
})
];
}
}
});
});
}
returnapplyRules;
}
module.exports=functioncreateDLayer(options){
@ -233,8 +266,10 @@ module.exports = function createDLayer(options) {
schema:options.schema
});
letevaluate=makeEnvironment(combinedContext);
// FIXME: Currently, top-level errors do not get a path property assigned to them, because that assignment happens on nested calls above