Remove direct execution feature flag - WIP (#19254)
Bug fixes exposed by always-on direct execution
1. GraphQL spec compliance — data[field] = null on resolver error
direct-execution.service.ts — Changed from Promise.allSettled (which
lost the responseKey on rejection) to Promise.all with per-field
try/catch; errors now set data[responseKey] = null per spec
2. Empty object arguments skipped (extractArgumentsFromAst)
extract-arguments-from-ast.util.ts — Removed isEmptyObject check;
filter: {}, data: {} now correctly passed to resolvers instead of
silently dropped (which caused permissions to never be checked)
3. orderBy: {} factory default treated as "no ordering"
direct-execution.service.ts — Before calling the resolver, strips
orderBy: {} and orderByForRecords: {} (empty-object factory defaults
that mean "no ordering")
assert-find-many-args.util.ts / assert-group-by-args.util.ts — Accept {}
for orderBy without throwing
4. orderBy: { field: '...' } object auto-coerced to [{ field: '...' }]
array
direct-execution.service.ts — Applies GraphQL list coercion: a
non-array, non-empty orderBy object is wrapped in an array before
assertion and resolver call
5. totalCount and aggregate fields returned as strings from PostgreSQL
graphql-format-result-from-selected-fields.util.ts — Added
coerceAggregateValue that parses numeric strings to numbers for
totalCount, sum*, avg*, min*, max*, count*, percentageOf* fields
Test updates
nested-relation-queries.integration-spec.ts — Updated expected error
message from Yoga schema-validation message to direct execution resolver
message
~30 snapshot files — Updated to reflect direct execution's error
messages (different from Yoga schema-validation messages for input type
errors)
This commit is contained in:
+8
-7
@@ -59,13 +59,14 @@ export class CreateManyRecordsService {
|
||||
createdBy: actorMetadata,
|
||||
}));
|
||||
|
||||
const createdRecords = await this.commonCreateManyRunner.execute(
|
||||
{
|
||||
data: cleanedRecords,
|
||||
selectedFields,
|
||||
},
|
||||
queryRunnerContext,
|
||||
);
|
||||
const { results: createdRecords } =
|
||||
await this.commonCreateManyRunner.execute(
|
||||
{
|
||||
data: cleanedRecords,
|
||||
selectedFields,
|
||||
},
|
||||
queryRunnerContext,
|
||||
);
|
||||
|
||||
this.logger.log(
|
||||
`Created ${createdRecords.length} records in ${objectName}`,
|
||||
|
||||
+8
-7
@@ -61,13 +61,14 @@ export class CreateRecordService {
|
||||
const cleanedRecord = removeUndefinedFromRecord(objectRecord);
|
||||
const dataWithActor = { ...cleanedRecord, createdBy: actorMetadata };
|
||||
|
||||
const createdRecord = await this.commonCreateOneRunner.execute(
|
||||
{
|
||||
data: dataWithActor,
|
||||
selectedFields,
|
||||
},
|
||||
queryRunnerContext,
|
||||
);
|
||||
const { results: createdRecord } =
|
||||
await this.commonCreateOneRunner.execute(
|
||||
{
|
||||
data: dataWithActor,
|
||||
selectedFields,
|
||||
},
|
||||
queryRunnerContext,
|
||||
);
|
||||
|
||||
this.logger.log(`Record created successfully in ${objectName}`);
|
||||
|
||||
|
||||
+16
-14
@@ -54,13 +54,14 @@ export class DeleteRecordService {
|
||||
}
|
||||
|
||||
if (soft) {
|
||||
const deletedRecord = await this.commonDeleteOneRunner.execute(
|
||||
{
|
||||
id: objectRecordId,
|
||||
selectedFields,
|
||||
},
|
||||
queryRunnerContext,
|
||||
);
|
||||
const { results: deletedRecord } =
|
||||
await this.commonDeleteOneRunner.execute(
|
||||
{
|
||||
id: objectRecordId,
|
||||
selectedFields,
|
||||
},
|
||||
queryRunnerContext,
|
||||
);
|
||||
|
||||
this.logger.log(`Record soft deleted successfully from ${objectName}`);
|
||||
|
||||
@@ -70,13 +71,14 @@ export class DeleteRecordService {
|
||||
result: deletedRecord,
|
||||
};
|
||||
} else {
|
||||
const destroyedRecord = await this.commonDestroyOneRunner.execute(
|
||||
{
|
||||
id: objectRecordId,
|
||||
selectedFields,
|
||||
},
|
||||
queryRunnerContext,
|
||||
);
|
||||
const { results: destroyedRecord } =
|
||||
await this.commonDestroyOneRunner.execute(
|
||||
{
|
||||
id: objectRecordId,
|
||||
selectedFields,
|
||||
},
|
||||
queryRunnerContext,
|
||||
);
|
||||
|
||||
this.logger.log(
|
||||
`Record permanently deleted successfully from ${objectName}`,
|
||||
|
||||
+3
-1
@@ -50,7 +50,9 @@ export class FindRecordsService {
|
||||
{ id: OrderByDirection.AscNullsFirst },
|
||||
];
|
||||
|
||||
const { records, totalCount } = await this.commonFindManyRunner.execute(
|
||||
const {
|
||||
results: { records, totalCount },
|
||||
} = await this.commonFindManyRunner.execute(
|
||||
{
|
||||
filter,
|
||||
orderBy: orderByWithIdCondition,
|
||||
|
||||
+5
-4
@@ -50,10 +50,11 @@ export class UpdateManyRecordsService {
|
||||
|
||||
const cleanedData = removeUndefinedFromRecord(data);
|
||||
|
||||
const updatedRecords = await this.commonUpdateManyRunner.execute(
|
||||
{ filter, data: cleanedData, selectedFields },
|
||||
queryRunnerContext,
|
||||
);
|
||||
const { results: updatedRecords } =
|
||||
await this.commonUpdateManyRunner.execute(
|
||||
{ filter, data: cleanedData, selectedFields },
|
||||
queryRunnerContext,
|
||||
);
|
||||
|
||||
this.logger.log(
|
||||
`Updated ${updatedRecords.length} records in ${objectName}`,
|
||||
|
||||
+9
-8
@@ -89,14 +89,15 @@ export class UpdateRecordService {
|
||||
// This prevents validation errors for partial composite field inputs
|
||||
const cleanedRecord = removeUndefinedFromRecord(filteredObjectRecord);
|
||||
|
||||
const updatedRecord = await this.commonUpdateOneRunner.execute(
|
||||
{
|
||||
id: objectRecordId,
|
||||
data: cleanedRecord,
|
||||
selectedFields,
|
||||
},
|
||||
queryRunnerContext,
|
||||
);
|
||||
const { results: updatedRecord } =
|
||||
await this.commonUpdateOneRunner.execute(
|
||||
{
|
||||
id: objectRecordId,
|
||||
data: cleanedRecord,
|
||||
selectedFields,
|
||||
},
|
||||
queryRunnerContext,
|
||||
);
|
||||
|
||||
this.logger.log(`Record updated successfully in ${objectName}`);
|
||||
|
||||
|
||||
+9
-8
@@ -48,14 +48,15 @@ export class UpsertRecordService {
|
||||
const cleanedRecord = removeUndefinedFromRecord(objectRecord);
|
||||
|
||||
// Use Common API with upsert flag - it handles conflict detection automatically
|
||||
const upsertedRecord = await this.commonCreateOneRunner.execute(
|
||||
{
|
||||
data: cleanedRecord,
|
||||
selectedFields,
|
||||
upsert: true,
|
||||
},
|
||||
queryRunnerContext,
|
||||
);
|
||||
const { results: upsertedRecord } =
|
||||
await this.commonCreateOneRunner.execute(
|
||||
{
|
||||
data: cleanedRecord,
|
||||
selectedFields,
|
||||
upsert: true,
|
||||
},
|
||||
queryRunnerContext,
|
||||
);
|
||||
|
||||
this.logger.log(`Record upserted successfully in ${objectName}`);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user