Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions packages/bridge/test/shared-parity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1718,7 +1718,6 @@ regressionTest("parity: sparse fieldsets — wildcard and chains", {
},
fields: ["id", "legs.*"],
assertData: { id: 42, legs: { duration: "2h", distance: 150 } },
disable: ["graphql"],
assertTraces: 1,
},
"requesting price returns price": {
Expand Down Expand Up @@ -1883,7 +1882,6 @@ regressionTest("parity: sparse fieldsets — nested and array paths", {
{ id: 1, legs: [{ name: "L1" }] },
{ id: 2, legs: [{ name: "L2" }] },
],
disable: ["graphql"],
assertTraces: 1,
},
"all fields returned when no requestedFields": {
Expand Down
66 changes: 65 additions & 1 deletion packages/bridge/test/utils/regression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,50 @@ function buildSelectionTreeForValue(
return tree;
}

/**
* Expand wildcard (`*`) entries and bare leaf selections on object-typed
* fields by looking up sub-fields from the GraphQL schema type.
*
* - `{ legs: { "*": {} } }` → `{ legs: { duration: {}, distance: {} } }`
* - `{ legs: {} }` where `legs` is an object type → `{ legs: { name: {} } }`
*/
function resolveSelectionsAgainstSchema(
tree: SelectionTree,
type: GraphQLOutputType,
): SelectionTree {
const named = getNamedType(type);
if (!isObjectType(named)) return tree;

const result: SelectionTree = {};
for (const [key, child] of Object.entries(tree)) {
const fieldDef = named.getFields()[key];
if (!fieldDef) {
result[key] = child;
continue;
}

if ("*" in child) {
// Expand wildcard to all immediate sub-fields from the schema type
const expanded = buildSelectionTreeFromType(fieldDef.type);
result[key] = expanded ?? {};
} else if (Object.keys(child).length === 0) {
// Bare leaf — if the field's type has sub-fields, expand them so
// the resulting GraphQL query is valid.
const fieldNamedType = getNamedType(fieldDef.type);
if (isObjectType(fieldNamedType)) {
const expanded = buildSelectionTreeFromType(fieldDef.type);
result[key] = expanded ?? {};
} else {
result[key] = child;
}
} else {
result[key] = resolveSelectionsAgainstSchema(child, fieldDef.type);
}
}

return result;
}

function renderSelectionTree(tree: SelectionTree | null): string {
if (!tree || Object.keys(tree).length === 0) {
return "";
Expand Down Expand Up @@ -372,6 +416,17 @@ function mergeObservedSelection(

for (const selectedPath of selectedPaths) {
const path = selectedPath.split(".").filter(Boolean);

// Handle wildcard: "legs.*" means "select all children of legs"
if (path.length > 1 && path[path.length - 1] === "*") {
const parentPath = path.slice(0, -1);
const parentValue = getObservationPath(value, parentPath);
if (parentValue !== undefined) {
setObservationPath(merged, parentPath, parentValue);
}
continue;
}

const selectedValue = getObservationPath(value, path);
if (selectedValue !== undefined) {
setObservationPath(merged, path, selectedValue);
Expand Down Expand Up @@ -428,12 +483,21 @@ function buildGraphQLOperationSource(
? `(${field.args.map((arg) => `${arg.name}: $${arg.name}`).join(", ")})`
: "";

const selectionTree = orderSelectionTree(
let selectionTree = orderSelectionTree(
requestedFields?.length
? buildSelectionTreeFromPaths(requestedFields)
: buildSelectionTreeForValue(expectedData, field.type),
preferredFieldOrder,
);

// When explicit requestedFields are provided, resolve wildcards
// (e.g. "legs.*") and bare leaf selections on object-typed fields
// (e.g. "legs" where legs has sub-fields) against the schema type so
// the resulting GraphQL query includes the required sub-field selections.
if (selectionTree && requestedFields?.length) {
selectionTree = resolveSelectionsAgainstSchema(selectionTree, field.type);
}

const selection = renderSelectionTree(selectionTree);
const operationKeyword = rootTypeName === "Mutation" ? "mutation" : "query";

Expand Down
Loading