1
1
import { isAsyncIterable , Plugin } from '@envelop/core' ;
2
- import { ExecutionResult , GraphQLError , Kind , OperationTypeNode , ResponsePath } from 'graphql' ;
2
+ import { useOnResolve } from '@envelop/on-resolve' ;
3
+ import { GraphQLError , ResponsePath } from 'graphql' ;
3
4
import { google , Trace } from 'apollo-reporting-protobuf' ;
4
5
5
6
const ctxKey = Symbol ( 'ApolloInlineTracePluginContextKey' ) ;
6
- const errorsKey = Symbol ( 'ApolloInlineTracePluginErrorsKey' ) ;
7
7
8
8
interface ApolloInlineTracePluginContext {
9
9
startHrTime : [ number , number ] ;
@@ -49,7 +49,7 @@ export function useApolloInlineTrace<PluginContext extends Record<string, any> =
49
49
shouldTrace,
50
50
rewriteError,
51
51
} : ApolloInlineTracePluginOptions < PluginContext > ) : Plugin <
52
- PluginContext & { [ ctxKey ] : ApolloInlineTracePluginContext ; [ errorsKey ] : GraphQLError [ ] }
52
+ PluginContext & { [ ctxKey ] : ApolloInlineTracePluginContext }
53
53
> {
54
54
return {
55
55
onEnveloped ( { context, extendContext } ) {
@@ -73,142 +73,72 @@ export function useApolloInlineTrace<PluginContext extends Record<string, any> =
73
73
nodes : new Map ( [ [ responsePathToString ( ) , rootNode ] ] ) ,
74
74
stopped : false ,
75
75
} ,
76
- [ errorsKey ] : [ ] ,
77
76
} ) ;
78
77
}
79
78
} ,
80
- onResolverCalled ( { context, info } ) {
81
- const ctx = context [ ctxKey ] ;
82
- if ( ! ctx ) return ;
83
-
84
- // result was already shipped (see ApolloInlineTracePluginContext.stopped)
85
- if ( ctx . stopped ) {
86
- return ( ) => {
87
- // noop
88
- } ;
89
- }
90
-
91
- const node = newTraceNode ( ctx , info . path ) ;
92
- node . type = info . returnType . toString ( ) ;
93
- node . parentType = info . parentType . toString ( ) ;
94
- node . startTime = hrTimeToDurationInNanos ( process . hrtime ( ctx . startHrTime ) ) ;
95
- if ( typeof info . path . key === 'string' && info . path . key !== info . fieldName ) {
96
- // field was aliased, send the original field name too
97
- node . originalFieldName = info . fieldName ;
98
- }
79
+ onPluginInit ( { addPlugin } ) {
80
+ addPlugin (
81
+ useOnResolve ( ( { context, info } ) => {
82
+ const ctx = context [ ctxKey ] ;
83
+ if ( ! ctx ) return ;
99
84
100
- return ( ) => {
101
- node . endTime = hrTimeToDurationInNanos ( process . hrtime ( ctx . startHrTime ) ) ;
102
- } ;
103
- } ,
104
- onParse ( ) {
105
- return ( { context, result, replaceParseResult } ) => {
106
- const ctx = context [ ctxKey ] ;
107
- if ( ! ctx ) return ;
85
+ // result was already shipped (see ApolloInlineTracePluginContext.stopped)
86
+ if ( ctx . stopped ) {
87
+ return ( ) => {
88
+ // noop
89
+ } ;
90
+ }
108
91
109
- const errors = context [ errorsKey ] ;
110
- if ( errors && result instanceof Error ) {
111
- if ( result instanceof GraphQLError ) {
112
- errors . push ( result ) ;
113
- } else {
114
- errors . push (
115
- new GraphQLError ( result . message , {
116
- originalError : result ,
117
- } )
118
- ) ;
92
+ const node = newTraceNode ( ctx , info . path ) ;
93
+ node . type = info . returnType . toString ( ) ;
94
+ node . parentType = info . parentType . toString ( ) ;
95
+ node . startTime = hrTimeToDurationInNanos ( process . hrtime ( ctx . startHrTime ) ) ;
96
+ if ( typeof info . path . key === 'string' && info . path . key !== info . fieldName ) {
97
+ // field was aliased, send the original field name too
98
+ node . originalFieldName = info . fieldName ;
119
99
}
120
- replaceParseResult ( {
121
- kind : Kind . DOCUMENT ,
122
- definitions : [
123
- {
124
- kind : Kind . OPERATION_DEFINITION ,
125
- operation : OperationTypeNode . QUERY ,
126
- selectionSet : {
127
- kind : Kind . SELECTION_SET ,
128
- selections : [
129
- {
130
- kind : Kind . FIELD ,
131
- name : {
132
- kind : Kind . NAME ,
133
- value : '__typename' ,
134
- } ,
135
- } ,
136
- ] ,
137
- } ,
138
- } ,
139
- ] ,
140
- } ) ;
141
- }
142
- } ;
143
- } ,
144
- onValidate ( { context, setResult } ) {
145
- const errorsInContext = context [ errorsKey ] ;
146
- if ( errorsInContext ?. length ) {
147
- setResult ( [ ] ) ;
148
- }
149
- return ( { result : errors , setResult } ) => {
150
- if ( errorsInContext ) {
151
- errorsInContext . push ( ...errors ) ;
152
- setResult ( [ ] ) ;
153
- }
154
- } ;
100
+
101
+ return ( ) => {
102
+ node . endTime = hrTimeToDurationInNanos ( process . hrtime ( ctx . startHrTime ) ) ;
103
+ } ;
104
+ } )
105
+ ) ;
155
106
} ,
156
- onExecute ( { args : { contextValue } , setResultAndStopExecution } ) {
157
- const errors = contextValue [ errorsKey ] ;
158
- if ( errors ?. length ) {
159
- const ctx = contextValue [ ctxKey ] ;
160
- if ( ! ctx ) return ;
161
- const result = { errors } ;
162
- const updatedResult = updateResult ( result , rewriteError , ctx ) ;
163
- setResultAndStopExecution ( updatedResult ) ;
164
- return ;
165
- }
107
+ onPerform ( { context } ) {
166
108
return {
167
- onExecuteDone ( { args : { contextValue } , result, setResult } ) {
168
- const ctx = contextValue [ ctxKey ] ;
109
+ onPerformDone ( { result } ) {
110
+ const ctx = context [ ctxKey ] ;
169
111
if ( ! ctx ) return ;
170
112
171
113
// TODO: should handle streaming results? how?
172
114
if ( isAsyncIterable ( result ) ) return ;
173
115
174
- const updatedResult = updateResult ( result , rewriteError , ctx ) ;
175
-
176
- setResult ( updatedResult ) ;
177
- } ,
178
- } ;
179
- } ,
180
- } ;
181
- }
182
-
183
- function updateResult (
184
- result : ExecutionResult ,
185
- rewriteError : ApolloInlineTracePluginOptions [ 'rewriteError' ] ,
186
- ctx : ApolloInlineTracePluginContext
187
- ) {
188
- if ( result . extensions ?. ftv1 !== undefined ) {
189
- throw new Error ( 'The `ftv1` extension is already present' ) ;
190
- }
116
+ if ( result . extensions ?. ftv1 !== undefined ) {
117
+ throw new Error ( 'The `ftv1` extension is already present' ) ;
118
+ }
191
119
192
- if ( result . errors ?. length ) {
193
- handleErrors ( ctx , result . errors , rewriteError ) ;
194
- }
120
+ // onResultProcess will be called only once since we disallow async iterables
121
+ if ( ctx . stopped ) throw new Error ( 'Trace stopped multiple times' ) ;
195
122
196
- // onResultProcess will be called only once since we disallow async iterables
197
- if ( ctx . stopped ) throw new Error ( 'Trace stopped multiple times' ) ;
123
+ if ( result . errors ) {
124
+ handleErrors ( ctx , result . errors , rewriteError ) ;
125
+ }
198
126
199
- ctx . stopped = true ;
200
- ctx . trace . durationNs = hrTimeToDurationInNanos ( process . hrtime ( ctx . startHrTime ) ) ;
201
- ctx . trace . endTime = nowTimestamp ( ) ;
127
+ ctx . stopped = true ;
128
+ ctx . trace . durationNs = hrTimeToDurationInNanos ( process . hrtime ( ctx . startHrTime ) ) ;
129
+ ctx . trace . endTime = nowTimestamp ( ) ;
202
130
203
- const encodedUint8Array = Trace . encode ( ctx . trace ) . finish ( ) ;
204
- const encodedBuffer = Buffer . from ( encodedUint8Array , encodedUint8Array . byteOffset , encodedUint8Array . byteLength ) ;
131
+ const encodedUint8Array = Trace . encode ( ctx . trace ) . finish ( ) ;
132
+ const base64 = btoa ( String . fromCharCode ( ... encodedUint8Array ) ) ;
205
133
206
- result . extensions = {
207
- ...result . extensions ,
208
- ftv1 : encodedBuffer . toString ( 'base64' ) ,
134
+ result . extensions = {
135
+ ...result . extensions ,
136
+ ftv1 : base64 ,
137
+ } ;
138
+ } ,
139
+ } ;
140
+ } ,
209
141
} ;
210
-
211
- return result ;
212
142
}
213
143
214
144
/**
0 commit comments