Skip to content

Commit 649cec4

Browse files
fix(GraphQL): fix custom DQL (GRAPHQL-1020) (#7449)
This PR makes `@custom(dql: ...)` work again after the GraphQL response JSON generation changes went in and broke it in #7371.
1 parent fa2a161 commit 649cec4

File tree

3 files changed

+90
-29
lines changed

3 files changed

+90
-29
lines changed

graphql/e2e/custom_logic/custom_logic_test.go

-1
Original file line numberDiff line numberDiff line change
@@ -2569,7 +2569,6 @@ func TestRestCustomLogicInDeepNestedField(t *testing.T) {
25692569
}
25702570

25712571
func TestCustomDQL(t *testing.T) {
2572-
t.Skipf("enable after fixing @custom(dql: ...)")
25732572
common.SafelyDropAll(t)
25742573

25752574
schema := `

graphql/resolve/query.go

+89-27
Original file line numberDiff line numberDiff line change
@@ -112,37 +112,16 @@ func (qr *queryResolver) rewriteAndExecute(ctx context.Context, query schema.Que
112112
}
113113
}
114114

115-
var qry string
116-
vars := make(map[string]string)
117-
118-
// DQL queries don't need any rewriting, as they are already in DQL form
119-
if query.QueryType() == schema.DQLQuery {
120-
qry = query.DQLQuery()
121-
args := query.Arguments()
122-
for k, v := range args {
123-
// dgoapi.Request{}.Vars accepts only string values for variables,
124-
// so need to convert all variable values to string
125-
vStr, err := convertScalarToString(v)
126-
if err != nil {
127-
return emptyResult(schema.GQLWrapf(err, "couldn't convert argument %s to string",
128-
k))
129-
}
130-
// the keys in dgoapi.Request{}.Vars are assumed to be prefixed with $
131-
vars["$"+k] = vStr
132-
}
133-
} else {
134-
dgQuery, err := qr.queryRewriter.Rewrite(ctx, query)
135-
if err != nil {
136-
return emptyResult(schema.GQLWrapf(err, "couldn't rewrite query %s",
137-
query.ResponseName()))
138-
}
139-
qry = dgraph.AsString(dgQuery)
115+
dgQuery, err := qr.queryRewriter.Rewrite(ctx, query)
116+
if err != nil {
117+
return emptyResult(schema.GQLWrapf(err, "couldn't rewrite query %s",
118+
query.ResponseName()))
140119
}
120+
qry := dgraph.AsString(dgQuery)
141121

142122
queryTimer := newtimer(ctx, &dgraphQueryDuration.OffsetDuration)
143123
queryTimer.Start()
144-
resp, err := qr.executor.Execute(ctx, &dgoapi.Request{Query: qry, Vars: vars,
145-
ReadOnly: true}, query)
124+
resp, err := qr.executor.Execute(ctx, &dgoapi.Request{Query: qry, ReadOnly: true}, query)
146125
queryTimer.Stop()
147126

148127
if err != nil && !x.IsGqlErrorList(err) {
@@ -161,6 +140,89 @@ func (qr *queryResolver) rewriteAndExecute(ctx context.Context, query schema.Que
161140
return resolved
162141
}
163142

143+
func NewCustomDQLQueryResolver(ex DgraphExecutor) QueryResolver {
144+
return &customDQLQueryResolver{executor: ex}
145+
}
146+
147+
type customDQLQueryResolver struct {
148+
executor DgraphExecutor
149+
}
150+
151+
func (qr *customDQLQueryResolver) Resolve(ctx context.Context, query schema.Query) *Resolved {
152+
span := otrace.FromContext(ctx)
153+
stop := x.SpanTimer(span, "resolveCustomDQLQuery")
154+
defer stop()
155+
156+
resolverTrace := &schema.ResolverTrace{
157+
Path: []interface{}{query.ResponseName()},
158+
ParentType: "Query",
159+
FieldName: query.ResponseName(),
160+
ReturnType: query.Type().String(),
161+
}
162+
timer := newtimer(ctx, &resolverTrace.OffsetDuration)
163+
timer.Start()
164+
defer timer.Stop()
165+
166+
resolved := qr.rewriteAndExecute(ctx, query)
167+
resolverTrace.Dgraph = resolved.Extensions.Tracing.Execution.Resolvers[0].Dgraph
168+
resolved.Extensions.Tracing.Execution.Resolvers[0] = resolverTrace
169+
return resolved
170+
}
171+
172+
func (qr *customDQLQueryResolver) rewriteAndExecute(ctx context.Context,
173+
query schema.Query) *Resolved {
174+
dgraphQueryDuration := &schema.LabeledOffsetDuration{Label: "query"}
175+
ext := &schema.Extensions{
176+
Tracing: &schema.Trace{
177+
Execution: &schema.ExecutionTrace{
178+
Resolvers: []*schema.ResolverTrace{
179+
{Dgraph: []*schema.LabeledOffsetDuration{dgraphQueryDuration}},
180+
},
181+
},
182+
},
183+
}
184+
185+
emptyResult := func(err error) *Resolved {
186+
resolved := EmptyResult(query, err)
187+
resolved.Extensions = ext
188+
return resolved
189+
}
190+
191+
dgQuery := query.DQLQuery()
192+
args := query.Arguments()
193+
vars := make(map[string]string)
194+
for k, v := range args {
195+
// dgoapi.Request{}.Vars accepts only string values for variables,
196+
// so need to convert all variable values to string
197+
vStr, err := convertScalarToString(v)
198+
if err != nil {
199+
return emptyResult(schema.GQLWrapf(err, "couldn't convert argument %s to string", k))
200+
}
201+
// the keys in dgoapi.Request{}.Vars are assumed to be prefixed with $
202+
vars["$"+k] = vStr
203+
}
204+
205+
queryTimer := newtimer(ctx, &dgraphQueryDuration.OffsetDuration)
206+
queryTimer.Start()
207+
resp, err := qr.executor.Execute(ctx, &dgoapi.Request{Query: dgQuery, Vars: vars,
208+
ReadOnly: true}, nil)
209+
queryTimer.Stop()
210+
211+
if err != nil {
212+
return emptyResult(schema.GQLWrapf(err, "Dgraph query failed"))
213+
}
214+
ext.TouchedUids = resp.GetMetrics().GetNumUids()[touchedUidsKey]
215+
216+
var respJson map[string]interface{}
217+
if err = schema.Unmarshal(resp.Json, &respJson); err != nil {
218+
return emptyResult(schema.GQLWrapf(err, "couldn't unmarshal Dgraph result"))
219+
}
220+
221+
resolved := DataResult(query, respJson, nil)
222+
resolved.Extensions = ext
223+
return resolved
224+
}
225+
164226
func resolveIntrospection(ctx context.Context, q schema.Query) *Resolved {
165227
data, err := schema.Introspect(q)
166228
return &Resolved{

graphql/resolve/resolver.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ func (rf *resolverFactory) WithConventionResolvers(
236236
for _, q := range s.Queries(schema.DQLQuery) {
237237
rf.WithQueryResolver(q, func(q schema.Query) QueryResolver {
238238
// DQL queries don't need any QueryRewriter
239-
return NewQueryResolver(nil, fns.Ex)
239+
return NewCustomDQLQueryResolver(fns.Ex)
240240
})
241241
}
242242

0 commit comments

Comments
 (0)