@@ -4,6 +4,10 @@ import { arrayify, fill, isThenable, loadModule, logger } from '@sentry/utils';
44
55import { shouldDisableAutoInstrumentation } from './utils/node-utils' ;
66
7+ interface ApolloOptions {
8+ useNestjs ?: boolean ;
9+ }
10+
711type ApolloResolverGroup = {
812 [ key : string ] : ( ) => unknown ;
913} ;
@@ -24,6 +28,19 @@ export class Apollo implements Integration {
2428 */
2529 public name : string = Apollo . id ;
2630
31+ private readonly _useNest : boolean ;
32+
33+ /**
34+ * @inheritDoc
35+ */
36+ public constructor (
37+ options : ApolloOptions = {
38+ useNestjs : false ,
39+ } ,
40+ ) {
41+ this . _useNest = ! ! options . useNestjs ;
42+ }
43+
2744 /**
2845 * @inheritDoc
2946 */
@@ -33,62 +50,111 @@ export class Apollo implements Integration {
3350 return ;
3451 }
3552
36- const pkg = loadModule < {
37- ApolloServerBase : {
38- prototype : {
39- constructSchema : ( ) => unknown ;
53+ if ( this . _useNest ) {
54+ const pkg = loadModule < {
55+ GraphQLFactory : {
56+ prototype : {
57+ create : ( resolvers : ApolloModelResolvers [ ] ) => unknown ;
58+ } ;
4059 } ;
41- } ;
42- } > ( 'apollo-server-core' ) ;
60+ } > ( '@nestjs/graphql' ) ;
4361
44- if ( ! pkg ) {
45- __DEBUG_BUILD__ && logger . error ( 'Apollo Integration was unable to require apollo-server-core package.' ) ;
46- return ;
47- }
62+ if ( ! pkg ) {
63+ __DEBUG_BUILD__ && logger . error ( 'Apollo-NestJS Integration was unable to require @nestjs/graphql package.' ) ;
64+ return ;
65+ }
66+
67+ /**
68+ * Iterate over resolvers of NestJS ResolversExplorerService before schemas are constructed.
69+ */
70+ fill (
71+ pkg . GraphQLFactory . prototype ,
72+ 'mergeWithSchema' ,
73+ function ( orig : ( this : unknown , ...args : unknown [ ] ) => unknown ) {
74+ return function (
75+ this : { resolversExplorerService : { explore : ( ) => ApolloModelResolvers [ ] } } ,
76+ ...args : unknown [ ]
77+ ) {
78+ fill ( this . resolversExplorerService , 'explore' , function ( orig : ( ) => ApolloModelResolvers [ ] ) {
79+ return function ( this : unknown ) {
80+ const resolvers = arrayify ( orig . call ( this ) ) ;
81+
82+ const instrumentedResolvers = instrumentResolvers ( resolvers , getCurrentHub ) ;
83+
84+ return instrumentedResolvers ;
85+ } ;
86+ } ) ;
87+
88+ return orig . call ( this , ...args ) ;
89+ } ;
90+ } ,
91+ ) ;
92+ } else {
93+ const pkg = loadModule < {
94+ ApolloServerBase : {
95+ prototype : {
96+ constructSchema : ( config : unknown ) => unknown ;
97+ } ;
98+ } ;
99+ } > ( 'apollo-server-core' ) ;
100+
101+ if ( ! pkg ) {
102+ __DEBUG_BUILD__ && logger . error ( 'Apollo Integration was unable to require apollo-server-core package.' ) ;
103+ return ;
104+ }
105+
106+ /**
107+ * Iterate over resolvers of the ApolloServer instance before schemas are constructed.
108+ */
109+ fill ( pkg . ApolloServerBase . prototype , 'constructSchema' , function ( orig : ( config : unknown ) => unknown ) {
110+ return function ( this : {
111+ config : { resolvers ?: ApolloModelResolvers [ ] ; schema ?: unknown ; modules ?: unknown } ;
112+ } ) {
113+ if ( ! this . config . resolvers ) {
114+ if ( __DEBUG_BUILD__ ) {
115+ if ( this . config . schema ) {
116+ logger . warn (
117+ 'Apollo integration is not able to trace `ApolloServer` instances constructed via `schema` property.' +
118+ 'If you are using NestJS with Apollo, please use `Sentry.Integrations.Apollo({ useNestjs: true })` instead.' ,
119+ ) ;
120+ logger . warn ( ) ;
121+ } else if ( this . config . modules ) {
122+ logger . warn (
123+ 'Apollo integration is not able to trace `ApolloServer` instances constructed via `modules` property.' ,
124+ ) ;
125+ }
48126
49- /**
50- * Iterate over resolvers of the ApolloServer instance before schemas are constructed.
51- */
52- fill ( pkg . ApolloServerBase . prototype , 'constructSchema' , function ( orig : ( ) => unknown ) {
53- return function ( this : { config : { resolvers ?: ApolloModelResolvers [ ] ; schema ?: unknown ; modules ?: unknown } } ) {
54- if ( ! this . config . resolvers ) {
55- if ( __DEBUG_BUILD__ ) {
56- if ( this . config . schema ) {
57- logger . warn (
58- 'Apollo integration is not able to trace `ApolloServer` instances constructed via `schema` property.' ,
59- ) ;
60- } else if ( this . config . modules ) {
61- logger . warn (
62- 'Apollo integration is not able to trace `ApolloServer` instances constructed via `modules` property.' ,
63- ) ;
127+ logger . error ( 'Skipping tracing as no resolvers found on the `ApolloServer` instance.' ) ;
64128 }
65129
66- logger . error ( 'Skipping tracing as no resolvers found on the `ApolloServer` instance.' ) ;
130+ return orig . call ( this ) ;
67131 }
68132
69- return orig . call ( this ) ;
70- }
133+ const resolvers = arrayify ( this . config . resolvers ) ;
71134
72- const resolvers = arrayify ( this . config . resolvers ) ;
73-
74- this . config . resolvers = resolvers . map ( model => {
75- Object . keys ( model ) . forEach ( resolverGroupName => {
76- Object . keys ( model [ resolverGroupName ] ) . forEach ( resolverName => {
77- if ( typeof model [ resolverGroupName ] [ resolverName ] !== 'function' ) {
78- return ;
79- }
135+ this . config . resolvers = instrumentResolvers ( resolvers , getCurrentHub ) ;
80136
81- wrapResolver ( model , resolverGroupName , resolverName , getCurrentHub ) ;
82- } ) ;
83- } ) ;
137+ return orig . call ( this ) ;
138+ } ;
139+ } ) ;
140+ }
141+ }
142+ }
84143
85- return model ;
86- } ) ;
144+ function instrumentResolvers ( resolvers : ApolloModelResolvers [ ] , getCurrentHub : ( ) => Hub ) : ApolloModelResolvers [ ] {
145+ return resolvers . map ( model => {
146+ Object . keys ( model ) . forEach ( resolverGroupName => {
147+ Object . keys ( model [ resolverGroupName ] ) . forEach ( resolverName => {
148+ if ( typeof model [ resolverGroupName ] [ resolverName ] !== 'function' ) {
149+ return ;
150+ }
87151
88- return orig . call ( this ) ;
89- } ;
152+ wrapResolver ( model , resolverGroupName , resolverName , getCurrentHub ) ;
153+ } ) ;
90154 } ) ;
91- }
155+
156+ return model ;
157+ } ) ;
92158}
93159
94160/**
0 commit comments