From 8f14dd330bc6aa544305536fb3ff8f8dbf23cb30 Mon Sep 17 00:00:00 2001 From: Valentin Cocaud Date: Mon, 10 Feb 2025 11:49:53 +0100 Subject: [PATCH] [otel] rely on context for parenting spans correctly --- ...h_plugin-opentelemetry-532-dependencies.md | 7 + e2e/opentelemetry/opentelemetry.e2e.ts | 119 ++-- examples/apq-subgraphs/example.tar.gz | Bin 69370 -> 69370 bytes examples/apq-subgraphs/package-lock.json | 8 +- examples/apq-subgraphs/package.json | 2 +- examples/extra-fields/example.tar.gz | Bin 58451 -> 58450 bytes examples/extra-fields/package-lock.json | 8 +- examples/extra-fields/package.json | 2 +- examples/federation-example/example.tar.gz | Bin 70710 -> 70711 bytes examples/federation-example/package-lock.json | 8 +- examples/federation-example/package.json | 2 +- examples/federation-mixed/example.tar.gz | Bin 77290 -> 77290 bytes examples/federation-mixed/package-lock.json | 8 +- examples/federation-mixed/package.json | 2 +- .../example.tar.gz | Bin 71924 -> 71925 bytes .../package-lock.json | 8 +- .../package.json | 2 +- examples/file-upload/example.tar.gz | Bin 58357 -> 58357 bytes examples/file-upload/package-lock.json | 8 +- examples/file-upload/package.json | 2 +- examples/hmac-auth-https/example.tar.gz | Bin 71758 -> 71760 bytes examples/hmac-auth-https/package-lock.json | 8 +- examples/hmac-auth-https/package.json | 2 +- .../example.tar.gz | Bin 58757 -> 58757 bytes .../package-lock.json | 8 +- .../package.json | 2 +- .../json-schema-subscriptions/example.tar.gz | Bin 63454 -> 63454 bytes .../package-lock.json | 8 +- .../json-schema-subscriptions/package.json | 2 +- .../example.tar.gz | Bin 58244 -> 58244 bytes .../package-lock.json | 8 +- .../openapi-additional-resolvers/package.json | 2 +- examples/openapi-arg-rename/example.tar.gz | Bin 61757 -> 61757 bytes examples/openapi-arg-rename/package-lock.json | 8 +- examples/openapi-arg-rename/package.json | 2 +- .../openapi-javascript-wiki/example.tar.gz | Bin 58525 -> 58525 bytes .../openapi-javascript-wiki/package-lock.json | 8 +- examples/openapi-javascript-wiki/package.json | 2 +- examples/openapi-subscriptions/example.tar.gz | Bin 63451 -> 63451 bytes .../openapi-subscriptions/package-lock.json | 8 +- examples/openapi-subscriptions/package.json | 2 +- .../example.tar.gz | Bin 58726 -> 58726 bytes .../package-lock.json | 8 +- .../operation-field-permissions/package.json | 2 +- examples/programmatic-batching/example.tar.gz | Bin 62468 -> 62467 bytes .../programmatic-batching/package-lock.json | 8 +- examples/programmatic-batching/package.json | 2 +- .../example.tar.gz | Bin 58870 -> 58869 bytes .../package-lock.json | 8 +- .../package.json | 2 +- examples/type-merging-batching/example.tar.gz | Bin 58934 -> 58934 bytes .../type-merging-batching/package-lock.json | 8 +- examples/type-merging-batching/package.json | 2 +- package.json | 1 + packages/plugins/opentelemetry/package.json | 2 + .../opentelemetry/src/contextManager.ts | 42 ++ .../plugins/opentelemetry/src/plugin-utils.ts | 104 +++ packages/plugins/opentelemetry/src/plugin.ts | 608 +++++++++++------- packages/plugins/opentelemetry/src/spans.ts | 326 ++++++---- packages/plugins/opentelemetry/src/utils.ts | 48 ++ yarn.lock | 31 +- 61 files changed, 978 insertions(+), 480 deletions(-) create mode 100644 .changeset/@graphql-mesh_plugin-opentelemetry-532-dependencies.md create mode 100644 packages/plugins/opentelemetry/src/contextManager.ts create mode 100644 packages/plugins/opentelemetry/src/plugin-utils.ts create mode 100644 packages/plugins/opentelemetry/src/utils.ts diff --git a/.changeset/@graphql-mesh_plugin-opentelemetry-532-dependencies.md b/.changeset/@graphql-mesh_plugin-opentelemetry-532-dependencies.md new file mode 100644 index 000000000..ff6e3308c --- /dev/null +++ b/.changeset/@graphql-mesh_plugin-opentelemetry-532-dependencies.md @@ -0,0 +1,7 @@ +--- +'@graphql-mesh/plugin-opentelemetry': patch +--- + +dependencies updates: + +- Added dependency [`@opentelemetry/context-async-hooks@^1.30.1` ↗︎](https://www.npmjs.com/package/@opentelemetry/context-async-hooks/v/1.30.1) (to `dependencies`) diff --git a/e2e/opentelemetry/opentelemetry.e2e.ts b/e2e/opentelemetry/opentelemetry.e2e.ts index b16a1b2ee..498056c79 100644 --- a/e2e/opentelemetry/opentelemetry.e2e.ts +++ b/e2e/opentelemetry/opentelemetry.e2e.ts @@ -24,15 +24,18 @@ beforeAll(async () => { type JaegerTracesApiResponse = { data: Array<{ traceID: string; - spans: Array<{ - traceID: string; - spanID: string; - operationName: string; - tags: Array<{ key: string; value: string; type: string }>; - }>; + spans: JaegerTraceSpan[]; }>; }; +type JaegerTraceSpan = { + traceID: string; + spanID: string; + operationName: string; + tags: Array<{ key: string; value: string; type: string }>; + references: Array<{ refType: string; spanID: string; traceID: string }>; +}; + describe('OpenTelemetry', () => { (['grpc', 'http'] as const).forEach((OTLP_EXPORTER_TYPE) => { describe(`exporter > ${OTLP_EXPORTER_TYPE}`, () => { @@ -77,6 +80,9 @@ describe('OpenTelemetry', () => { await checkFn(res); return; } catch (e) { + if (signal.aborted) { + throw err; + } err = e; } } @@ -559,40 +565,52 @@ describe('OpenTelemetry', () => { expect(relevantTraces.length).toBe(1); const relevantTrace = relevantTraces[0]; expect(relevantTrace).toBeDefined(); - expect(relevantTrace?.spans.length).toBe(11); + expect(relevantTrace!.spans.length).toBe(18); - expect(relevantTrace?.spans).toContainEqual( - expect.objectContaining({ operationName: 'POST /graphql' }), - ); - expect(relevantTrace?.spans).toContainEqual( - expect.objectContaining({ operationName: 'graphql.parse' }), - ); - expect(relevantTrace?.spans).toContainEqual( - expect.objectContaining({ operationName: 'graphql.validate' }), - ); - expect(relevantTrace?.spans).toContainEqual( - expect.objectContaining({ operationName: 'graphql.execute' }), + const spanTree = buildSpanTree(relevantTrace!.spans, 'POST /graphql'); + expect(spanTree).toBeDefined(); + + const expectedHttpChildren = [ + 'graphql.parse', + 'graphql.validate', + 'graphql.execute', + ]; + expect(spanTree!.children).toHaveLength(3); + for (const operationName of expectedHttpChildren) { + expect(spanTree?.children).toContainEqual( + expect.objectContaining({ + span: expect.objectContaining({ operationName }), + }), + ); + } + + const executeSpan = spanTree?.children.find( + ({ span }) => span.operationName === 'graphql.execute', ); - expect( - relevantTrace?.spans.filter( - (r) => r.operationName === 'subgraph.execute (accounts)', - ).length, - ).toBe(2); - expect( - relevantTrace?.spans.filter( - (r) => r.operationName === 'subgraph.execute (products)', - ).length, - ).toBe(2); - expect( - relevantTrace?.spans.filter( - (r) => r.operationName === 'subgraph.execute (inventory)', - ).length, - ).toBe(1); - expect( - relevantTrace?.spans.filter( - (r) => r.operationName === 'subgraph.execute (reviews)', - ).length, - ).toBe(2); + + const expectedExecuteChildren = [ + ['subgraph.execute (accounts)', 2], + ['subgraph.execute (products)', 2], + ['subgraph.execute (inventory)', 1], + ['subgraph.execute (reviews)', 2], + ] as const; + + for (const [operationName, count] of expectedExecuteChildren) { + const matchingChildren = executeSpan!.children.filter( + ({ span }) => span.operationName === operationName, + ); + expect(matchingChildren).toHaveLength(count); + for (const child of matchingChildren) { + expect(child.children).toHaveLength(1); + expect(child.children).toContainEqual( + expect.objectContaining({ + span: expect.objectContaining({ + operationName: 'http.fetch', + }), + }), + ); + } + } }); }); @@ -1279,3 +1297,28 @@ describe('OpenTelemetry', () => { }); }); }); + +type TraceTreeNode = { + span: JaegerTraceSpan; + children: TraceTreeNode[]; +}; +function buildSpanTree( + spans: JaegerTraceSpan[], + rootName: string, +): TraceTreeNode | undefined { + function buildNode(root: JaegerTraceSpan): TraceTreeNode { + return { + span: root, + children: spans + .filter((span) => + span.references.find( + (ref) => ref.refType === 'CHILD_OF' && ref.spanID === root.spanID, + ), + ) + .map(buildNode), + }; + } + + const root = spans.find((span) => span.operationName === rootName); + return root && buildNode(root); +} diff --git a/examples/apq-subgraphs/example.tar.gz b/examples/apq-subgraphs/example.tar.gz index 9a0cb17a5ab66b69b02a061f90fe370e62a7172f..a6b544a4513fad33c4e219158a9f0425c70381d4 100644 GIT binary patch delta 413 zcmV;O0b>68odo)w1c0;wYz=>9p?&3DA(r>N7xhl+8&4o#R*ocj@uZxx>iE+OhZ`<< zyz}I_&*dGepFEpbzWwUTNU59cr5__*i1U)}!aKjG%Bl{>u-o#SQqEkN+F(|FY+Pa~bdPf2>@-X#al_R>pGv|2;r8 zk_e)O{}#xU36D}9T==sfJC;%Sbk3ST{qo`XctSOAlxCDlgwQwNJ7k_po{Ld@5MrIg zD{wE%`?X+7!C4hPCrmbxqwA9Fekp)I64%T#R$F> zMyWyOD3Sh=aM6RF;<$g${5yT~%jK(|e=Pj^pTeiWa&+<2&nFEj{`cpPapkXm_ve=% z^K*(aS&5O%7=0lj*q1^Hg5W)pCxP<><@0ALEh%|6A&8|*Pie;4`1&vLwOD?S$D#X_ zq*MG1$Cr}Q|LV??Lk>CQkV6hRCQkV6p;IpmPT-yZ%4y|_mt H0I&oAN*~<7 delta 413 zcmex0m*v-7mWC~iIlT3}M}ME%?WFU*QvcQ5E9UbY_N|fe{Pf7vGWF`m<>GwiF&`?P zpDdn#+B(F(((LD)M-TS|Y~flbzxmA;EC2KJKmG|_o~3$KNA6two{xp+EBEjEd;a+U zjnDYsCcl(8{&Du>|5x)R93NKvnJypIE&V}X{=fOH|EcHyJ~O)a_W z+K4ayp>V{iGyFl^wC~vmr!;NWs_~nVqvU>F_2%CohI*|nlk}_>aN9Sw+6S$haYvV@ z=HyM;)?n8z!5Yn;pyo4yN`I9fE@HPmT3A2-XZf#Z(@%%n|Bv|b)xOog@#%|4%jf$` z1^sz{UjFf^_`mPY>)StjE|R)QNO`8=FDIuB`mI7N2dh>5nVxf=dVW4AD(F*=lYsa1 zd6#-NfBYK%D0|}{xqj#Gnx4ylNcQQgF8zOXu4=nX(FsMYgdkXEzwICXik#`93>#P& E00wH-$p8QV diff --git a/examples/apq-subgraphs/package-lock.json b/examples/apq-subgraphs/package-lock.json index 05043a7de..1f5c01d1a 100644 --- a/examples/apq-subgraphs/package-lock.json +++ b/examples/apq-subgraphs/package-lock.json @@ -7,7 +7,7 @@ "name": "@example/apq-subgraphs", "dependencies": { "@apollo/server": "^4.11.2", - "@graphql-hive/gateway": "^1.9.0", + "@graphql-hive/gateway": "^1.9.1", "@graphql-mesh/compose-cli": "^1.2.13", "graphql": "^16.9.0", "tslib": "^2.8.1" @@ -1272,9 +1272,9 @@ } }, "node_modules/@graphql-hive/gateway": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@graphql-hive/gateway/-/gateway-1.9.0.tgz", - "integrity": "sha512-baTF35N3yFLLOx0+cFgencZ6cPLz18S38V6k6m/N34lOW356obYsarDrhecndYqJSeecuosAxs3CoVA+jAJnag==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@graphql-hive/gateway/-/gateway-1.9.1.tgz", + "integrity": "sha512-MyML8yKqYN9e5FRmsEpQ2R4Fd0jgpMX+0KEtd/I3UIDhtqVXAFHQ89F9b7A7LmV0OPCV9hpM2wLFBHwrfTXF7A==", "license": "MIT", "dependencies": { "@commander-js/extra-typings": "^13.0.0", diff --git a/examples/apq-subgraphs/package.json b/examples/apq-subgraphs/package.json index 34294fcfa..9b4250df3 100644 --- a/examples/apq-subgraphs/package.json +++ b/examples/apq-subgraphs/package.json @@ -9,7 +9,7 @@ "@graphql-mesh/compose-cli": "^1.2.13", "graphql": "^16.9.0", "tslib": "^2.8.1", - "@graphql-hive/gateway": "^1.9.0" + "@graphql-hive/gateway": "^1.9.1" }, "scripts": { "service:greetings": "tsx services/greetings.ts", diff --git a/examples/extra-fields/example.tar.gz b/examples/extra-fields/example.tar.gz index 7959037433210652906609e47cddbb849e984e18..96bdd694f2244ff621bfd568125c52c81df67622 100644 GIT binary patch delta 423 zcmV;Y0a*Ui$OF>I1F*Q_f4%YA%lqQs2<2uw#6RBd6F-1!On=8CvK;_6I4pg-Q?io-tg*3=8rg=j-B|Qri^O_AAj>4QtS#zp;xyT`<{3aDBPdk(-UOTet=!R z4Fg{6#(E9e&^E}m%SR0-u52LYzc_XI_w`0fLK0vBOpGi>e8oyA{!ShHN?_l8DGK?( zIu_8Efe9Meh_l!of2aGe))&Db&@B)^8fURe;g22v?)CddhVuk;-Po1koP@LcDPP|N z-k`9irK_+EQ7TvD6}G0PlmO~=GLfKeZbK0b57WQffrSx2 z!a4NMq$2wN4E)0M4;yh7D{9(N`iE{GL;o=4kEVYzB>ImK9U(%52oWMgh!7z{ga{EL RM2PTf<3G~W_HF>^0|2xI)y@C_ delta 424 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 V!7TwD4HPsC{{{EM^IHH=1ppH=Qt|)* diff --git a/examples/federation-mixed/package-lock.json b/examples/federation-mixed/package-lock.json index d25ee5afc..cece819f2 100644 --- a/examples/federation-mixed/package-lock.json +++ b/examples/federation-mixed/package-lock.json @@ -8,7 +8,7 @@ "dependencies": { "@apollo/server": "^4.10.3", "@apollo/subgraph": "^2.7.2", - "@graphql-hive/gateway": "^1.9.0", + "@graphql-hive/gateway": "^1.9.1", "@graphql-mesh/compose-cli": "^1.2.13", "@omnigraph/openapi": "^0.108.6", "fets": "^0.8.4", @@ -1338,9 +1338,9 @@ } }, "node_modules/@graphql-hive/gateway": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@graphql-hive/gateway/-/gateway-1.9.0.tgz", - "integrity": "sha512-baTF35N3yFLLOx0+cFgencZ6cPLz18S38V6k6m/N34lOW356obYsarDrhecndYqJSeecuosAxs3CoVA+jAJnag==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@graphql-hive/gateway/-/gateway-1.9.1.tgz", + "integrity": "sha512-MyML8yKqYN9e5FRmsEpQ2R4Fd0jgpMX+0KEtd/I3UIDhtqVXAFHQ89F9b7A7LmV0OPCV9hpM2wLFBHwrfTXF7A==", "license": "MIT", "dependencies": { "@commander-js/extra-typings": "^13.0.0", diff --git a/examples/federation-mixed/package.json b/examples/federation-mixed/package.json index 90e61cc78..babd35251 100644 --- a/examples/federation-mixed/package.json +++ b/examples/federation-mixed/package.json @@ -9,7 +9,7 @@ "@apollo/server": "^4.10.3", "@apollo/subgraph": "^2.7.2", "fets": "^0.8.4", - "@graphql-hive/gateway": "^1.9.0" + "@graphql-hive/gateway": "^1.9.1" }, "devDependencies": { "tsx": "^4.19.2", diff --git a/examples/federation-subscriptions-passthrough/example.tar.gz b/examples/federation-subscriptions-passthrough/example.tar.gz index 0b8d77f9ac22257610eba561ebb1acb117cbdc08..66ddd3f63e5bafc9f3c6e814e5c88aa9f14207e9 100644 GIT binary patch delta 4995 zcmV-}6MXFSu>|$81c0;wYykl|mu&$79uxBW?aM>=G3Lk1&)I{=3r^kCHwx;SRU_w{5Qd1RBBgx-J8T^^RyMNW(N z_4ejkX$m*lPTMlRacOlcbe@h>v-VAy*Yr z5E`Y@HYn-=HD1n2KFgZTrdF%Mfz`7l2cBw+@xrIq6O}~Q?`4*kZqVD}Jtt$u$8Z_~ zjF1`rWQ*yr$U-P+4`g#x?2yg2uDQj5loQ0+U`DsNRxsCi$3cwYW~vRQeSOx`Jppx> zecj6Y#Wjj(e5G$})rF3K4$8quvN)*>a^_b9K)gD6gnU#Jm{SJ-T4~N=97f$^`|Fd; z^)!(5LZb4Be2B*hU++DKk!_IGjL0jaBHm@(F*U!Pb6-LayY(_D87L{(_!6H4(0Jk2 zwl!keFCZ&dh0tzgsuh=0L-$71U_I!nDz}gp=rR~AEMrdAT>&XNl2M+K#=n0PH* zJ&d>gK@ThymD(2HLvTgY?2$Um-FR1z38@jl7@dZnRsb#c8|I94JVpM9;+Gx!WjT~q#VQRj8 zmsnTnM1Pli&0yz$F$TVa8DesGmzHtQn^euEku5%EJkDv51&FLIBv!209yYgT4NQ@^ zJkeiaC4wjq?P4h0w+3yQr^or`ijr)=5WLf&H-+hHo}*AcZ_LQ?qgt}PPV%z+s~%$+ zTegmlSJTKKE?`Ao&T^W%!}~owOzyG1xB?$_%T;1*pqrw9hp=`@%>`s>gd3X0)=>)a%V_6L_M}Cs~d}Nbs)am$c%PgE?k4(9O#$M zPhaZ-@ve31V@tpoVvP9kH|@f&+->E?GCH2+C`Tp`Ip=S43p_7EjH|ozt=T9qLLqzG zvs8AzGI~|W-w?)nhDdg;>IH3PF>h>Yvx2r_^iIE53;#AE`-a2fDbMAFLXZazYr!-z za3f@YXZJ{sBgUMhT)qCa$v4&1YcQ{xi;xeQiyWXvJ-ffplIe^^OpzGQT6i$&Z3{SC zqw5Z{vXr35HwKpG2_uLG0gCoCR^SLrfu_=glcXX$!zMrw#*OKO9Z069&@NIcVJnkd z5oo{iCmD;FmM(Js0nr%Z*m{1ll!K#OHWmDqLQNh8W5f0?J;Zx(xLN zZ_0a=#Wx+LKOGG5IlXA)OB2ht;JQduQT)%RudCC%SM-&?MS&M4u@4--z46L_ioSk- z*ulJ!yxzgo;*}2gVQ2f0o$V_aP2s=dhgX5jt9j%lVj z>M)JpIotB+ES13BPgBWL7Ec$NfbA1Sa#a_GbI1|h+=%dv#%#1zjXtK0q?$C@Y~*~q zujXrH!}HjU)yUOZqtE5MV(Y$y4uP8hGKEDeYc#955$;+EqJ< zwr2|?+Y+*5)?s8Z+5=B&D%`dRxvZ!MEasKU&W;gWXjczeUtar^bP&EyQa(zmcZe!# z>f28!MgT*qihg2QHO0|HL>KBo$Q!G(cC+0oDdX;l!m4g_!`BNYvC)c_9a#L(E3I4o z3h&TRPHnXMTZ%`JlD^)yM=;WVU6=Y38iyf*uCPUNF%VJ(%1yuMcm;z8)57=D2JT=+ zcueln>H!xkXEGMtt=JX>tx*v|;i6Wv`|5AeDfIq4+bJ`*Tr2 zl(I3N_rW%p){)Mvv1(F4^rvFIZuF~fuoW%KHW)Bc_TtRhd)QX`jIp|ZdzzMbAs$fs z#UNuOhrihbNQzKJJRJn1Ap})AYQF_bMn_$YrbN5bp(gWQZH=Sh8mrk^6%}JvTL<;o zx~_VZ*N`E+$W_sSjpr(8u0NqFs?y>NXIrs*_&4fWIa##DgP?iF#V(Eoq35~L8TiQm zBQUapC?EBNXMVK}u(d^h17!K|KhDw&e^(5>RwP}h0-gI!HtzKN-%+9Q7uH_*S}0zGoEg~n&MqJi z?F62{jb`QJQ|wD41rIZ(lIfUgxcq3t+PcBv-imVlJ_**K;+bN%M=G9OgDqGYOt)>| zQuY8<#I8PW4#m-bYTT)Gf!EFVK&d`uR+dRLmHzTwys^K-zR08AX?^>?2ZK%3f z3v&*$jCNNIatCXF*2F&QPp4ZifHYy zz5f|_jH1f`PD2$jeTT#^I*BfAugthTq(6LK8H~Q(%2@@NYuGZv*)a06mUYeDs0s(( z-g^4O=iS+@i2TEkvADhOa`Y%meAoeub5Q)Hc$m?D&I~t>z70NnW^_%KH8^(bX6#4% z@l@5ZfX&9hcOZ^Rkq@7ZuyJ8~jg1NKZx1rr@$(a|6!8PF^TKti!P@8Vn!eW#emE^3 zoAX>YHEWuC-9q8`)%#3$UlWQStJ>EQakV2N_OGl(2f(Ya5iXNxBhV0yu~715*Oksq z#qZvKcK`P?R|yCF8QLDdA*aKWQ>zKUgp^JIMST%Y4Zns=l()I$sA~v3vr9MOeFhv( ztH65+OME6yPLE=F&qbU zD@;wi9Gtunv&1k%vn}1WPBZq@x$mSSk|U%OXLrvhZ+u7?`gb1xsrdf^r;pW_4G{q# z6plCdKNc28fAs(NxE}2PJyT6p$Cp770UUpl5e!Yq{PMCBl79K^=bz)8r+nB?&o3Mk z%3@DXz%ZUOpZ*z7;#{=umBC9Kbu`ZTEldq5B_tU0B?_wdgoa69!oUA?V}D#%*Ms9f zp^lUc;49-F&*gKM;~x)M!jJL)9j?RpHvq%eMH$e(${pB%V1^9fP=qt0^$s0({l|Yy z@_Q&6DWpydZ!}RoWJVrbPBTmKI^hDs`SR?v_-WR_)5} zo=)M#TcZbHyOqaj0ow!@Z9Og;;mCiFUsxbqj-I|U11zm^60j~_j7$9-eneC(;{8@; zJm}Q=nNMM!<|;O~HbF=!R#Ys>HxtZ!x`nT&$Wl953r36LDiF+HllDBFQsh%aPEUXQ z!HY(utoWGuWnX%~(E8;$OtdY5{N?#MS##~wrG|Obs8I?3I#sd#@yD**2nc_`2-msb zDGA>DYlcz%=d&Y1VXJIIJxj`DUS3|37d~Dl@44lR5Q;@sW8h7)0e`(i<@SQz={zv@Re}%++ zSv>l-r)wzp&~QQ#AHyP%>f%5K@nhy68SM19+5!L1H!2q)=(5dWqL6=i5;Olie))?G z)Rnb5sV{szWnL@Vx^t1M0ijB!Bsz89X)>-us+>z5=J|Ahbj z>-oS$7+wFr_O7hAX&?x{=T~ehByUif!|nm*s#IQhKtdFWhpH-W;u5Q_9c3Lssq){M z*?ZljsZ$67b{-nX+3|m@cjww~ye{-tIzMX8PmCXbK2)w~;@M&b5j z>x$S53|%n^`c8Hfba_78Lj9X0?6@onu-|m7FAHKWlboe5qmrS(-6n;{S zKv~F((p%I^JOaTVl38-iVCeLO;*>A#fj#GLiXOb5=T$b%Q!{@GmIy}hO)}S0xK?1z z*qwadRhE9ATqjp47&rvyYLb#6AoA1Q+iH=;u+zutEn@x6AE->^h?wFrI*b`h$N4XT_+T;Zu5e$?3EQ#!7>} z$~3yl4RQ3BcDi>HR4TgA;lkzE;*85aQ!0}VD-xOM>*2GiX~FNeOWbIsOgS&V(l2th z!K`NUqR5J`1Z@qj6ZiXhIZEjq3#GRzqJZX_ zj4gw>77Tv_F^!m)!Pig8MV`u2GRi%B{#@IH#TdGb$0ekWF5@s<&KcD>CrLJQIrsNe z1o5cDy|m%_zn*>HD`Or07Y*v`|Gi-}@az9=Q2HG_&6vABv83R3X0Ijqav&y<&VlVm z;CD~uPiN>IIHSED;QUK7V9D0jZ1My-x=L?AOqzd>A>Qp|;~f!a(A3|(ZMT%2pYUh@ z+AVG5(53d{pMdzkshRz&nrbr{8`OUehM}+jY=btN|3>AXP3k}4Vd(w;HmFJcC-n87 ze~vb@|3>AXb@m?~MhA8K-yit=Z!6TK{&R0hho#l0>^d#y#);r_yEDFMz%?&>L;>h7 z{9%8moX1I?UnHZeHH93ugvGf7^m8XwQj8OP*!TY85Yz2`ML}p)0q1Whph%yf${VZ8 z4YFbHGRqO+r{X)0BjT`EtBRPf8&xAl<-DlI<+oyG4Z=m4o)UWAg~{NPu!!Z60-S8n zRnTQQBUDu&u;~jpdQArKvAcU0a=*a^4#|J&fV)Dhl=aYvgY9+`84>L>Jc?7s;i*O*mWyUH|H=INPq2VNQkqe1wNmUf>EO<-mrh{&2 zg}zllml`}aDrX2!4Qk+2og-|_5jEym-Kz&dUz^4MM)1$t{2!uyE&fM``#%5Q3fX`7 z&%^K-a)dk(Me>$*{@@Ef@}u^dfp^3iBAf?TO$pen0FAW2ro!)H>kj|$AkS~3huOdT zyn9m_>+FAT7}oKhXg~7yzYXI1e~c2GTl)`Rg?dlTj!Igsog3DU4ZV9&Bnj>oHpO~w z?VXJKeev%}1~EQYs!CJKw@aE09*-xK6Rwcc`V$*3JbCiu$&)8fo;-Q-|z71c0;wYykn;mu&$79uvy@?aM>=G3Lk1&)I{=3r^kC5sx;SRU_w{5Qd1RBBgx-J4T^^RyMNW(N z_4ejdjje% z`?{6)i)$3o_)6c{stX-|9h8HSWN}g%GZcFsBUuwcMP=IE=c-_SYww z>uDhAg+%2M`4Ep^e7*M^Mz%p#Ga|2yig=fC$JG3G&V30z?AFUy$v{cL#+UdcfW`~A zwyhD%egRpzDui|`Q?0n18oD>42J1mrRk?+?h3GfQJ1VGIVvb^#l&mj z>S4U?4|-s!sMNOj9)c^HW{=ci?#8=%Oh}CY#^^Nsv;t_k-(Y85fk(iEV%W7bpG*~G zL)x}8s`=w}L32h7%=?-Pm$xN$+4d_?L6|_TU^obrQYTn!7o$>()<$c7Xyia^2~+d! zyTrOmC;GeGYX&=ik1_Ba%n*~iyR?jZ-lS?SjcoBT<8e-dEI?#!A+ch`_OQ7%Yha4R z<%#|ZD-lF_Xct4_zBOpeJUz}gSCnJ}hTxqJy(vsr^Bjfpd1FS7AJvlWb&{9)U-cNv z*s^tWyqZP^aRDpxa+cH79p3NhVRDc4#TEFdTdrQl2D&MKdI)Qm)LcN8M!2!*khDT- z#;Agb6T*BZ_Y<^DVTp;p2;NbS8n=qyVE7}MPPGOeeIMruV)IfFknf^ zW57LzkYCM zYxI5R?2iT5Jc2zUHgeCNGPtJ6)Cz=Z>z;%=x#l8QfHpzdT`-8zp?zdNBHOgLq})~4 zAbb;Pm4kJ|5lbjMo;2EBg`RFrQfG@L)oE4cHG9y1?CQqiTOEk+HZr4~mkZb6HwXHq z^V8S5K)h?6`q&aMh8QD0{7t*?D|cJDv5byqIm(d~Q7+T@#R>NS{G%|*zE%ta1Rqn_R0XUTNNBBn?TXDvLK^tJ_@ ztGfZVJ>_dM!SPNW#F)7RB$-Yfdb-=e?^lh_B2-`@D-KSf`E zKkQ)MNM7$?YVk@3{IIiq$jQMq@VGszx8vMp8|hY&LSf z-BWPaa*9Wbj1A79*n7cuVzf`Kj}2syUDmGczT-@ zaA&@l%+z08mE(wwSR%SEm&VQd>$ z)4B^7R4gj^QX0~Yg2VQ0q(F0`wMq(5G(N;(K%Cn+B#)jLEL zHTCT$6eEBkRYgCsteWCzBBBfRAmokJS-aV8m6UOJL}698x#8;tlh|lQ%ML7l=#|#3 zeuZ~vD5o}B{Vl~KNJ(FB+anl%>8?xt35~-LL08x!xflqk0_CP(bi9JWgK6RWX#;mK zBRnQ|Y4w1Ml`|O&?$+ms$$Z)~$`IXB%;|zw#xyyHE!wd7{<7CndXP#$!%+Mg&HcHk zAWGR7&--8-OzTKz)>t(uAo^3WUN`#HH`t1nWg83_DSL6|>^*ENea2XS-91f9ybuql z{bG;tF43j zY+YA9%4^6FUgWCiz{YbGG}oU{6;)|*hO@2MJ^UMWt(+{{;z7_n<6;-bg3$BS=nQ=1 z{}C8jL6nbr!ZW|x2H4tvq5-me_#bC!hQG^(UMrF=Q~}tz;&;bw5&`I}h@=aSp@DzH z?wnA14;}tRVK?>2=vnLDL{U4)2f|7ovkAzr)0Z{X&Gw9_@*BXF#JbM5$|%Tfz1GmI z;eCzb3NRS+2Ftc8`8Cp=nzOa4F}W3_U`e3H;3YXXf^Isy1?t^d!SUGGAqj@no589F5cMRVPE7?@3g*s--ARBdnAT$ z2r=p&J8~frLr8IK532)|RUyzdtp-^gjqv)IaT--9#I@@Sie|5yW^1xyO4WeeIQ0%Y zfQ31SSw_3FTtS6ed9%*18cI%_z*bIEJMz4F*BM93;qrxlWmq7?hOJs+vilghvMHPg zvHp^ngUK47gd2!A&cc7hnOJ(uOFYwjw8%BUJAYid#nsGuUhldlO;fTJICxeMu;OENWjy#MO?7*nhGX9RRPwMz~C(jX+~Ge%SG)NKJK9 z@w>Nw-T&>(Rl)&(hPKCV$m#Ip)M^4SA*B;QQD1~p!!IEdKX#i?B1L3J^>D= zRp7mZIX<0#X#htA?)8Ve_gCMAlXb|9V?4(7%$Rn_zGIAJc4?Vx7RU~k-A!7b8IA+G z6~0Zp9GrX+v&1k#vn}1WP80Ujx$mSSk|U%OXLrvhUwlj$`gb1xsrdf^r;nAF4G{q# z6pT0bKNe<3fAs(NxE}2PJyT6p#+N}60UUoLBN&>J`T1ohB>nvBhYxYeQ$Fmc=NFC% zWwECxU>MJt&;N`kaV}c-%HSoAIvS_^8omuFB_tU0B?_wdgodyF2><@mjs0<5T@Q}` zggR0(fUk^yJeSX1j(7iB>EDtBN5f*CS^Ll(}6);o0E^`Cz- z$?qX+q>wtzywODUkQjMzIZZ6Z>x2sk=gYIx?7M|gPvm(yl$s52=bQbCD}S#nO14*` zS~VC^ZHD!IwF0mnXNLLn+^3*pw`lZku}E`kpfK0KcFCvTjdm-?W9A#n%Hs#SR_*fc zo=)M#TcZbHyOqaj2HOM|Z9Og;;mCiFUzj0Wj-Gxp11zm^60j~ljB|YmD-mUjc)yhy z4?4Ae=5v^)xr)uLO%PIw6=h4(%>*-_Z{h1HveZu2g3+S53Iy|)q&-ik6!{d9)6?(2 z^P&+cD?Vj@-sj#gw0?dLU)q*H{`~x$thsjTQo}rI)To4iovPS=|9zKl1O$IzgzH@J zlmzenCBvxx^Vt!huvNC9p1sOsUS3|34?bPK-gCrbI< zqUi^DLjbPeSm8crzUQunkc!tePNn+nMrl;*H|fVnD_7aouhMdG2VikrB^s%uACM^LK# zcV_lpH)-k=f`FZe#&LgkJnP-L_8YG&ogX#lC&rJzav~H34%6%#(~VHiYTk`cqj3AN zbw%t2hOU?deJ48#x;&q4q5e%0c3c((*l#-4mjyAGNzT$2QoM9hpG_~cOQzZZ3O^}E zpe*D?=`HFd9)aKw$t<~MFm!rCamttWz@BqAMGxN3^D3L>sTqF-O9UhMCYkFgTr03< z>`p%KDoejlu9K@23><=UHAzV_uO{Uz`@vI7G67c(cbzf3&NM;rYfLU~cuR?dS@%=F z-#5L}penD@A~+*$@RmH@*!L&>nH+JqXRD?M;cC@Z@|#l&JqdXUk;uqeGugK860LLH zmeE6IYoVQ*2o-u`EH^mg=_frb71TEYDB)l5WWm+I~VX@$^T^ zsQVG&0&2S58)t0FN6of(%dhFMW*hsNv?!Ta2-%nOf~$d8oKpNHCgQc_ofv~TzMJHw z5Gx$IbV{Nlf{S+p^z*1;_=fL(O z@Vlq-r!({poY7tnaQ>wkuw-j%HhF>^U8Of5Ce44x5bt)f@s5ZyXzK6Ywp+^1Px!Nc z?Upuj=u-RfPeAO|_Yf4eCD!!_e1%wn3ZCf1~oxCiS22F!cU^8`Px!6Z-nk zKS!I{f1~oxI{Oa~ql3Er?+<+bw-stq|GBrM!_w+gcAb`U<3#Ye-5Fmr;F^~`q5yOk z{;+>j&f_G{FOt#KnnDg+!s6Tk`ni)TDaHvt?0bK4i0O8}q9C-Yfb%yLP^3>#<&D+l z2HCK8ndOM^Q}Lb05pme7RYlC#jj9o&a$Z#9@>{X82H~PiPYFHm!esDCSj6&30ZumP zD(JGD5vnQ>*z^S)y(RRdhh%?sz+E9$%6e$T!FIcejmSIo0Xac2LZ)!9 z8XLnQ$o<-v;shKSl}8t^J3uLcOPEMk_2}Pn_@k; z_D;tAzWDbfgBYJHRi&xr+a=8gk4GlT30KHz{fUhio;-Q-1p1F#6+1hH(5W|IuxGJhHxU*8gD$?M|I=qH7A;ebgsr%0z!&@tr} zvPU?t$<6a~|2mm0b85Fq&2&!v(jOIC9M>D(b*d6eBh#nBi5H4~Ta}(`w^B{Ddv;w; zvKK;yzOq=ROf%hKudFIfL0>$Utq%JSKlc;lsWlB0g^1MIaBk7jRQz=X4p##8)PMa{ z4kzX!`)F+2jmP&Vziy#tqahzX>EMoOkt*Cr5bO(vO5fpUJI3_vMrB)PX%E}Ie!b-2 z#qxF`%kH^_y*Zj2t=mFn@&JX>v^F0%FKNg;+RbaD%67G$kf-2aqL0VTa!$&$N5-wB zYCIz*m25+hFPqjweiDSufXT0)j;UmhiH*WPVQCN5s<#z)ZHwzM)aP=9Qo zZfQ1RnEn!m>5eB)4AM7ckS;Z(UZkAZ`#YXV<{;PGTDf>s4$quVuJ@2(`e3>BFR zElQK>0&9!Cr)BpXO0aAj({|@vv8^g)Qp(lnu?3%c))lQx+w(!=dfFEUTz|O@?RKsM zOSKU%nnt0abiW=YY~vB!6^F*FT3WkT-Y`fxJV1N0@X{OfKjoFM$9DqR8l=Z+61EMq zeqQPK+1h|#)GJBU)=$fOMYE)a(Hx3=sq%dOu&8n4E9(AE$eMbK`zj{$)yoGdC)O2a zI;2cSQ#tvrv2zjAzU$=2{C^nARJeyKc5Kr5Xbebi+jm);xN%7xe->Wd9Z`#;$;dpR zU#6x4kJ*b1Dz9pStf?*&nMkl+Jj>vR)Q0w44^;nXsLF8d$YyZ%gRS0E{I@zic?-Zs zPs*Ch8b6`tyfEWMn0_!c^_X>J5LlLVjtHB_44Vtm-0O5Umtjvp=6{$>?1E*Oybi$6 zsOl{Ozp;U%{xh_BWiCT%?X7S+L(JQfEEcYH*xvtRpGBRYOOJ9&frfJVcp;5iaxq;X1*!1@&dbVF%v1$WIU z=CfDa&fi~iUPrC>0lfy8Rj?|0zlxydoF^i5n9{U#)ya}Jp>06M6Cc;oS6g`SeykGs zi2o1p1F#6+1d(iwXOj%yGJkqCzP=^QlGnwZ(N7BL!U2r^F{My5}L6E76~wkkc>Zl#)P_w2fy zWG{pYePyvsnP$4fURhO|g1&exTOIZve(op8Q)?P13K6NZ;oPF5src&(9IgcFsek*a z98Szd_R-k38;|c#e%(UPMngV&(!qVFMXGRbL9j0vDt(8a?HJRu8Q}nYegu7#~4%+tSO#K>e|S zx~1EQVfsrLraSIDF-YH(LAunCdXaKs@9(%LnS)$!Yvtm%a(L!^a=nKP(?>I7VW`Ml zXi=I}7g$^DJuSQEP=aOKn6^9TifvUXlTxlmk1hDrv#w}m+MW*@*VDc@;D5?(Xt#46 zSgMV9(KHGTrTg_LVH=O&t~fMa)zaF%@`gdm;Q`u{g_qu-|0%D8J-!pj)*wArldx@= z_47)<&(;R~qFza&zJ6NXE1D%WjOI|}OO@yIheeGWUs3mWLe|t<+*dJ~uUd1AA8hrW;=k4D$y)$6 zdQ#S0*7ylE=Y<(B!t{fosmH7%gTS(^b41uYX4qVi=3b|>xeR*(GJnTpVizpKX%3m4CXg-g=~q(sEf6YmG?zi{U(R~~PRt<#kLnutHW zB`5)b8&Q8PBMu;d1YTcpYzZ^yjDAE4ysHM~UQQzlk2SBQI3sR($~y-s4Hy;@G7jfy zzr}tBge|}=N_ppN6fsv;{CoF*ef$TlJ8~U(P!|e?o#X$-Z<;PJ`4f;oG9O#C5Bv?&wmK33=mP+i CiW7bS diff --git a/examples/file-upload/package-lock.json b/examples/file-upload/package-lock.json index 439ea26f8..5fbcf879c 100644 --- a/examples/file-upload/package-lock.json +++ b/examples/file-upload/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "@example/file-upload", "dependencies": { - "@graphql-hive/gateway": "^1.9.0", + "@graphql-hive/gateway": "^1.9.1", "@graphql-mesh/compose-cli": "^1.2.13", "graphql": "^16.9.0", "tslib": "^2.8.1" @@ -1058,9 +1058,9 @@ } }, "node_modules/@graphql-hive/gateway": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@graphql-hive/gateway/-/gateway-1.9.0.tgz", - "integrity": "sha512-baTF35N3yFLLOx0+cFgencZ6cPLz18S38V6k6m/N34lOW356obYsarDrhecndYqJSeecuosAxs3CoVA+jAJnag==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@graphql-hive/gateway/-/gateway-1.9.1.tgz", + "integrity": "sha512-MyML8yKqYN9e5FRmsEpQ2R4Fd0jgpMX+0KEtd/I3UIDhtqVXAFHQ89F9b7A7LmV0OPCV9hpM2wLFBHwrfTXF7A==", "license": "MIT", "dependencies": { "@commander-js/extra-typings": "^13.0.0", diff --git a/examples/file-upload/package.json b/examples/file-upload/package.json index 9534c8f6c..409375278 100644 --- a/examples/file-upload/package.json +++ b/examples/file-upload/package.json @@ -5,7 +5,7 @@ "@graphql-mesh/compose-cli": "^1.2.13", "graphql": "^16.9.0", "tslib": "^2.8.1", - "@graphql-hive/gateway": "^1.9.0" + "@graphql-hive/gateway": "^1.9.1" }, "devDependencies": { "tsx": "^4.19.2" diff --git a/examples/hmac-auth-https/example.tar.gz b/examples/hmac-auth-https/example.tar.gz index 6bdaddb2640d05256e4fcaea6bf5897e9dd81ccf..eb8ac08bfb1066be212e4787e1e0ed027b02e7c4 100644 GIT binary patch delta 1735 zcmV;&1~~c7umsSs1c0;wbP)mgmvs>VG=HPm_~C%eGy}^FO)s;Gp}A)FvTmFW!{_MS z-8=y}*y;bacZ-lMa^F4Y|HmQmjyxt|>Nt+yo;c1e{88ZJb$`qi z+wpKH{8^A}Q!jkv?a*?~k3WBZEEJa!nu;bep>ZlEIp;+-reYkk5;qIynU!?aX3|yt zr;9f_T^5fOF9m>e5DB?lCJnEbY#p=Ock1oK@FnHWL8Ojwn7!n{@Up10&jIYS&+7@I z#Cn=bvo@1U2VYoA&2ZxfpUP73>p`Q^kqQ8md!}OuOL9VnHz^^9BTD z58;20J&JLvNMSNa_VoHCl`mK)84t-hoanf9MPL#defr~&Quy=7y%>@~Ie+3JuD&nw z6!$CM3?xPf|MT?Ek16g>C`&q!R9B?Jr+*xie>`sJUD-DMLcDh12V4nH4zpS~V64m&n3&S|1^%0DJ$ zW&Zr}=kxXzBc^oRxv{uqPJgJ`LwnY_AAZ>9oM!B&kJ&aUCSm&1e`h)^szbGYXBxF} zWAgFGqvGQ!a~tsK8tPez%q#P!j~wfW*nir)+Ml$6Fz#pmii0fSnvseKAwf|$oyq*P z>E^N!f~+l$jI^djjWPW1dtUC^QpyUt5RLbIp|8)~J@>r*o;#pfwSQ8w&+TZr+Z9in zld(@*Jd!z~EOsWM;dC60Abrj85c`j_gIFCwX~Lm1J-eT-qmDHcI~u|zeo1mpIpeD~ z8Ca7t6Ff8 zcI;Fwi}hO-RKqB#xqW4WNlI|qxpjoJME4***H#o4@V3-G!f}e*EHg&>n1SrbFXRR|8L60w!^15yo=-{zaik-f^?HMJ5v|z9Uv`$D?YH3s zbsPGX_+BlHt79z(9p}&EVKjSWq%edj+-!mWHYkypUmZsYHshX6klHe73cq1dtbkeg8UzH{bnZrZ?|?^ zCjZ|AQDk#fkO1pD!hg+>WSigtQVU|yjpsx*XUDed-EEIN7mK4psQp-_-?!rSD5eqjMV|dv zvV^qM{<)I;U)or|{I=B-vmf6+QT#jRjQ^Cv24>^X-f0lt4@1{; z;VHyh28SPT2S|0H(4+2t41QJTOE}0I zm?f7AqQ&Gv;)rcT}e=vT2f$=fMwFgp?#wdzi8IUJ%}@LytME1FU&w$ojg7 znSXciTFRwi*2|rN6tW}!tyXH6L99c#OkeblUdmT5QSZh+H d$dDmJh71`pWXO;qLx$cO{RK1LBs&1u1ORqjam)Y! delta 1720 zcmV;p21ohOumsMq1c0;wbP)mQmvs>VG=Ec%4#-S1u*}f(GOHMxYj!W|#@R4@j?Uf9 z6M%!A{%?D?2-za{-E;nb91`!yV-lv0e&j0Vy|Hu-) zyp6Zhzl7q@H~N2#&+GI*ED4Xd@rwLcLJ;hV{uoq7zsY}JaT%ehXd)9Dr(%+GUSwk`#xW~#vv8hSNmp$qUDbcO zc%##0@mTRv05}JckjrJ#@OsJCF{^#2-aZUpQtli?>IjF~OAZV#i#q!pz&`uDo*+uB zr@1t1v$_*Etfamq&Kl1nAe;kCkAGf{)%UgI0^jqd;kbpv6fFFbn#283P=wEgkMPHz z3T`Z%#$Q_r%?h!^;{GE}OlGlZe?FCqA*WKoJ^?vZoXD)9It9tJ3$82{6hks^KtT2o z{`c6U7^jL9CWB;8uU}I6f_0MdketJbj$2m*CZW-%KMpB{KY!edAsLh-E`Q?c`yx+q zzv9h6VubKNPyhUw;_ig9qytHHMJjyy$1(ZGp|nNW8>nSCOW75V^UV; z&mVt2Z(lKDO2?fWi(BS|ntwgCXPx`uhkedz#(w&kZIfaWra%36rqiN2RO@%9Q5!cV zAAdY5KAtkS0iUj+o|VYFGJpEWv5tt9{?p#o{-h0raX<4{9ApXCj8sGj35vSuOy;Le zHwn~VJ*CcA>pSsAz>^%3OnZ5W{#*_Q?l{CM0VtS$TBE+I1s7?@ zPSvtlzg0msjFOt$S2mcW1gD)_M@UO_58`ueMR5V|JIEvNMTuCqCI+~45>Kg>dXUX4 zn0WRixRzDqW5+I4cTv#x0yk#RsV2PkQmjG`wAPOsw@2d%K!15D)rIsBY<8pe*cDx{ z4eTZyr?|~BW2BE6$d3F%Zh(`KikUb(+#=)oq*M?`I-*&xH%J%Jif#O5X9?PV8%|KS zpvsXE0R-N2u_uw zKi)&7CMrj2dscMe1o#7feR$3)l~Aj+cM$ljgb+G$A%7fH#%_h(I3}T=Ps(t_Y3l`r zOgz_#D*E(tX{OlWSuw99@bpys<$hUYx+x?2fM;?Gv0|8KNgjm-SFcH29~|8IiUDeW-e%v4-R zZB{M-J%6?I0@{c`Cq|u@{M`TuK2NT`fZIO%@$D1Ezhln$Pbq9*HV*Bb2I2iMbUhcI zLY$Y^N@g)ch8zjS9QLGgS>%R z(mH`a00l6R>tqo(ftj2OER}y;5{W$EiN&S73V(#Id(5) zvFkiN-ipwviuW{H82fTeT@e4D5B|1Rz<>2@{D<_fY2yDTXyf<~FQLBSzvVb!d+r<} zTn)l;a8q!w2)dEg;O3nr*bP~6vvb`l#SbpChj;txT&iAJ{L(8yd6zL50TU1m88T$( OtH5*zEtni^{oY_P{~L%6@UYFs)3;XA(nGyUJg z|0O(T_i>gz*#Ez|rxFYue*+RPX*6j1#ruEI@9*;ONo_nM|EV|?f7|)~EJe|0`Trx5 zq~J&=;49#Dh{Q<;M?2BM6ToMVf-9_gq_LWL_4~!{le`EO?!Ei#0ZC=A*0!id6)%uZ zglpo{olJl;!H2DCAuK)(8(P{2JLJe!tlj0{$Vdb79VlSXttxm&f-kNrlBW$-6 zCux`lrQBOjV{&gWe>~7bj143_)+u;v+3~6X-~loXI=16sKfsm+_2m`>Ou3K?<~nV| z_sT5P_*NOl#i!PNapb+%3pd&z2D7aje*Ww})I~U?K}Z{nQ8CKKI5|i=-V*S14L8!HAO$gv!%3;*@PB3}H^Wj^H^KsD|Q5PP}(9!T4cj(qtHsH6i2>?caa?{tLML zi@*$*fHMK+U1TB28qdk1W|-;UzG6sEq|2i=sZqa~fIsLHoABKs* Ii~#5Z046L3?*IS* delta 518 zcmV+h0{Q)g%L9eW1F($af5$QTyKP-e)!YfYvNUKLg#Nih$96c3776Vd)#qF z;N}@5&WxDG8ZDO!_%Tcs!u<^h4Hy=ZBsSKOH8s{G*kF&}hH!sx)wpy7!gqZCXZpW~ z|4Vqx?&BXH^PbC;S{stso(rD21i}(Ma-{0lmliGMj{!?)(f41}eS&E|1^8ZI9 zNx_j$z*oTQ5Q&oxj&`DhCxFi!1y@-0NMkkg>i3J?CwUPn+dP$%m~tT(%yrs^ z@0D4o@vSn9i%+fl;>dfi7jCpc3}#z5{QTK{sEcq&gOD~DqhgeeadMD$yd~i28hrQz zpz_G3aIJQF`ltO8>7BPgVC}jgKin&f_SJivR96(bakDd6e9ON=?d?FfFHWw)8520X zwNJrGgApei2$iR6#3|cM7{Z)#9l>)fPz}YAoOthIg7L%5q{%QMYeL8&+Q0w&{TFcg z7l9cr0cQftyU0S4HJ+12%`nryeZ`QTNS8-#Qlpk}>;HFGJg0Lyr*k@|bNXTFKX(e< IegNnL01aLUmH+?% diff --git a/examples/interface-additional-resolvers/package-lock.json b/examples/interface-additional-resolvers/package-lock.json index 11e042671..68f16158f 100644 --- a/examples/interface-additional-resolvers/package-lock.json +++ b/examples/interface-additional-resolvers/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "@example/interface-additional-resolvers", "dependencies": { - "@graphql-hive/gateway": "^1.9.0", + "@graphql-hive/gateway": "^1.9.1", "@graphql-mesh/compose-cli": "^1.2.13", "graphql": "^16.9.0", "graphql-yoga": "^5.10.11", @@ -1059,9 +1059,9 @@ } }, "node_modules/@graphql-hive/gateway": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@graphql-hive/gateway/-/gateway-1.9.0.tgz", - "integrity": "sha512-baTF35N3yFLLOx0+cFgencZ6cPLz18S38V6k6m/N34lOW356obYsarDrhecndYqJSeecuosAxs3CoVA+jAJnag==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@graphql-hive/gateway/-/gateway-1.9.1.tgz", + "integrity": "sha512-MyML8yKqYN9e5FRmsEpQ2R4Fd0jgpMX+0KEtd/I3UIDhtqVXAFHQ89F9b7A7LmV0OPCV9hpM2wLFBHwrfTXF7A==", "license": "MIT", "dependencies": { "@commander-js/extra-typings": "^13.0.0", diff --git a/examples/interface-additional-resolvers/package.json b/examples/interface-additional-resolvers/package.json index f2dcf8f0c..7b94ca3eb 100644 --- a/examples/interface-additional-resolvers/package.json +++ b/examples/interface-additional-resolvers/package.json @@ -6,7 +6,7 @@ "graphql": "^16.9.0", "graphql-yoga": "^5.10.11", "tslib": "^2.8.1", - "@graphql-hive/gateway": "^1.9.0" + "@graphql-hive/gateway": "^1.9.1" }, "devDependencies": { "tsx": "^4.19.2" diff --git a/examples/json-schema-subscriptions/example.tar.gz b/examples/json-schema-subscriptions/example.tar.gz index 06a48976b0aa9d29d6bd32e53849d46da6637f85..f2efb27ac8aef8e1b1067f1880172ff6cb72ddc7 100644 GIT binary patch delta 1778 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 delta 85 zcmZoU&fIdGd4uI+?_DYVNz4A;nDRbL^a}Ic?pLlX$2Fb&Bl(t`k2`d8!;-{)hg>_P=!b0aWlK@CWllu{05e7t9O*!=x_E diff --git a/examples/openapi-additional-resolvers/package-lock.json b/examples/openapi-additional-resolvers/package-lock.json index 50dd95183..63e1b5bbc 100644 --- a/examples/openapi-additional-resolvers/package-lock.json +++ b/examples/openapi-additional-resolvers/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "@example/openapi-additional-resolvers", "dependencies": { - "@graphql-hive/gateway": "^1.9.0", + "@graphql-hive/gateway": "^1.9.1", "@graphql-mesh/compose-cli": "^1.2.13", "@omnigraph/openapi": "^0.108.6", "graphql": "^16.9.0", @@ -649,9 +649,9 @@ } }, "node_modules/@graphql-hive/gateway": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@graphql-hive/gateway/-/gateway-1.9.0.tgz", - "integrity": "sha512-baTF35N3yFLLOx0+cFgencZ6cPLz18S38V6k6m/N34lOW356obYsarDrhecndYqJSeecuosAxs3CoVA+jAJnag==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@graphql-hive/gateway/-/gateway-1.9.1.tgz", + "integrity": "sha512-MyML8yKqYN9e5FRmsEpQ2R4Fd0jgpMX+0KEtd/I3UIDhtqVXAFHQ89F9b7A7LmV0OPCV9hpM2wLFBHwrfTXF7A==", "license": "MIT", "dependencies": { "@commander-js/extra-typings": "^13.0.0", diff --git a/examples/openapi-additional-resolvers/package.json b/examples/openapi-additional-resolvers/package.json index 436d80443..d06d5cfb9 100644 --- a/examples/openapi-additional-resolvers/package.json +++ b/examples/openapi-additional-resolvers/package.json @@ -7,7 +7,7 @@ "graphql": "^16.9.0", "moment": "^2.30.1", "tslib": "^2.8.1", - "@graphql-hive/gateway": "^1.9.0" + "@graphql-hive/gateway": "^1.9.1" }, "scripts": { "compose": "mesh-compose -o supergraph.graphql", diff --git a/examples/openapi-arg-rename/example.tar.gz b/examples/openapi-arg-rename/example.tar.gz index 7b977b6e5bef1383ba6f580356bbdf5c7b7bb2c8..1e00654a6ff151d0a94fd3f29d5d808b4eabcb87 100644 GIT binary patch delta 396 zcmV;70dxMn;sd?n1F)0te{=L7hcDY*)LS0?NB(ti*LixN$hvt_FpI*Ga>8Hd`X>LT z^udS2HNTo&d`wFpUh6=ktXM{~%HjN>;yn0a%5n0yemqj)NR;Qd&%UN|I6ryfYr#0K zw*Q6k|MZ5R)yMKNj^kIy|EpsNfkEUS59nvTz+3PiKK|co|3eS`e{db|@_z_=(f+j> zir)GEj{x1uVg@~A0_KD2Io89sObGckJOB3EmoHiB_8{k3iudr(yx61cDJh%b@@qf+ z>9G{$PknC0JQmskUETlm?}zLG z<=2OzmWMxnekvb?p8eaGLV&vRW|H^=UM#ER+IwXWN2!diWVQZO18aGyBg(@`@xUMH q*_Wa@91cV<%GY^^-caw z>4Oi4YkoDm_?VVHyw-t6S+R^}mBaZ%#d+|Vl9LKMY|5wKl0)xmu9?;Kvfw$m4eEh%F{)Zm=f8jdb<^K@$qWx<% z6utBR9|5|R#SD7L1k4B3bF7DNnGo`8cK+?RFJH3M?Lp476z}1md9g>?Q&Kj=<=1}t z$y2t!JfyPf&kz6n@0{=H>O*$%F%_Qw=P~7;{yY_%fBL35&g$FGKOb0Q`oN>F1e7AK zQY7EfYJ!x9=Wb`;e|~xRCc17aLW9-{MR0}^lQht-dC;5Zd+bCwX^QTlH^0JkLSwe|A}!3 hQJ9rize8#Mhg<)5**jo@Kej)_-A+v5Wq85N004z(D@yeGP0B1zo+`%>Ue1-wQ8Etjv!yE=aA3=e}qlM=iF%m{# zJv&a_o7z@59g2Mz%OWX>D+@g3laTq_aLU9`n#GwZn7cv8u#2LuC`|=nn7+}&Tp?KF zF}z3>sTE5;U1$_De~nmZ^S%j%ljE~?fdU*_Ly7n5XbkbJT!y_cpaC5n(IpPegZA;k zu?YdE5*%+>2w>`SZwR!53pFm$_%NOQ^KQ=~ZLXJV>TD?S%F;zG1&6o`e3K^NiL%+su3f9CZ-LvBW=pq%@n!WSdP-ilokOtZQ?A0kowmJiB&a3#xNKc>z8^-hloRI_Xwq$Vn#>%aTu0 zWLVUTQZ=+}DUfFCqMY7<&8$B212dBapzxY|A@f4LjgnZv%%}{m)3b;pWfGQ5LG#;b zpBGcfQqEASgN#(I;xR$YXJ4sz%?+Krr&zw&3SG6jgk(ftGJ_{$l7uzwZrPo<)mx7KxKo^ni5(e%C7?nzu zo+a$8hCNv<(KiR!1%$Q@THFh&2`(RMo&A${Y6u7tp9GtGKo;68mtBrZWAh+1pIuWy z^A6Tee>AMJ|99~J5lU$`a_{G3R``FzXlCbsT9tZj?f)MGqRutthK(Df=H>?3|IlS7 zC{qS+&HF;b*HizUlc`qE_qSkQAFv}+J@)UF9V(IAa;jl#!6z+?g4}LjK(Zd46Xm zDVhUzSdh&GNr$|cs7jUYH79vMn>~akx;!T*HJXMqAzsVRy;-1(eTaQ}FOicc#pS

mm-1z0__1cB3aS#GJ_nJ75m ze-ppQK++g@n>3aQ_7!TCY9Yz6Jn0a3cq1w%mpY3=zkb^LXS;uu04x)MSl-W))*Mw~ z0dG0Aq&d{5c%9?f#5Y3yJVDVjE@T1aHX(#e<~)oH(HQ0iF!3BzC~*47Kxg1!#E;U< zrvZ#C&l<83X(a|qqF)mgroAf8q5r zr7k53*`AiuT{|zNJ2z=z737-9Tq#JAIksjlw-zNYSHmQ%(lZImv`s>e&YdiwB+9Vd zQY*M!Sya7hY_k;Q4pJ>a*>SOP2o<~(Hy$P3uhJY-ssK%Dt&msj9bCfiXtg7o4Gw83 zslId&fz2mwU&Xz`Y#g=QCm=&~R-ms}a*K4N-H@Ke3Y2T_r|fxNBaDY=#2u(63O$ad z!W&YnY5PNz2bM2&G$%3fu|Yn_?O}q)dpEm=HLPI`Ygoe?*06>(tYHlg34a4ck0VL| GI0OLKZ_BFy delta 1504 zcmV<61t0p`?*rTK1F#VCf658bdBl#(+<8#7yyaW8nkdd7S)4$m4QJ4@nh_r`A4HFE zz&Liw=!@7@DGU^85pxNbn5{?mpmPW&XvGBOCB-PkotqX=i}dI|kz-RDqkmgO*YFQQ zzWtA-?Lq1wquy$@mf8Qs>#9*{8Bd_`AhoZ6|GE8N(f?#^e!7l(fAoLTXsrAHG2jmU zue{B@>sS&0)mlcj|C{w%YaRbR1~?)CPY-qg0j=}_#$SQbf1Tv^~LpM=cchEpbn(k#wQ!Q2fxhFuhOMQJJs!}N_F<_f_Y zkKskCNUd1%=|ZEJe`&-*oA*s9oE)FE3l!kc8cMuZM`MU*1NK8ger_1?Aiq6~1_x z7*7tc9h%ERaIm(@bNu-Qs`R_HRZP9SmIQWre};(f&yGKr;Z0J;n0HRvI}h{QZf@>w|Xz1WUPExpcayA*Wn0lJ86moRWIz^GKJ z^eka#HSEb^iM~0&E+DjR(BfWDO>p^8>+GMrQ$s+I_$1ie1G3O&x$JUO8k+~9`Rtks zns=~%f1+WP{lA0%k5Edpk$XQMv%>!yMl(DA)2h^KYybZk5OuC8H*DM>H8(fN{)a9z zL76gmBPWJ1C7Q}ML2R|KL4G!%9Lm$0LR-cpijgAqSu9zm(w8m^z=EFZpk0HIqjvR} zbIHg;>wKVh(bqyBeaeknoS;8?g{I7m6}2bLf1;T*#Tm_hC+dx@MpDK7t& zgMc*IXgs)N4@_<5Gr?nwbzB#^3=NDImH<9YC`e%F_`@mpFTm=dB?z3Z%W_NY&P2fh zf1mh029n0O+oZ8fu&+?7R0~Ol;QDnV&iqRmeQIW$!b9qv)U*#Spb5n5W|p(W`>;?4aylSE9i%A)F3W1FQYcaUlc%8rYTL#W`TxbZ0IewF5!QUz#IYlXaG@8A-CN2?v#Y;Z_R zN%f_J2y8xa`zr1gX5*;cJ^>k`Rs(&#l3S!B?S}L$R-jyaKV{GJ8eu#{Bkn*oQRs0r z72c3qP1_%$Jg|JJqdAF*j}7ucZVwYY-n-c~tYHmnSi>6Du!c3PVGV0|NcbB9aLGde GI0OLjq1#FT diff --git a/examples/openapi-subscriptions/package-lock.json b/examples/openapi-subscriptions/package-lock.json index 6b7246a70..10bdf8abb 100644 --- a/examples/openapi-subscriptions/package-lock.json +++ b/examples/openapi-subscriptions/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "@example/openapi-subscriptions", "dependencies": { - "@graphql-hive/gateway": "^1.9.0", + "@graphql-hive/gateway": "^1.9.1", "@graphql-mesh/compose-cli": "^1.2.13", "@omnigraph/openapi": "^0.108.6", "fets": "^0.8.4", @@ -1062,9 +1062,9 @@ } }, "node_modules/@graphql-hive/gateway": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@graphql-hive/gateway/-/gateway-1.9.0.tgz", - "integrity": "sha512-baTF35N3yFLLOx0+cFgencZ6cPLz18S38V6k6m/N34lOW356obYsarDrhecndYqJSeecuosAxs3CoVA+jAJnag==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@graphql-hive/gateway/-/gateway-1.9.1.tgz", + "integrity": "sha512-MyML8yKqYN9e5FRmsEpQ2R4Fd0jgpMX+0KEtd/I3UIDhtqVXAFHQ89F9b7A7LmV0OPCV9hpM2wLFBHwrfTXF7A==", "license": "MIT", "dependencies": { "@commander-js/extra-typings": "^13.0.0", diff --git a/examples/openapi-subscriptions/package.json b/examples/openapi-subscriptions/package.json index ec49570ea..37858ebce 100644 --- a/examples/openapi-subscriptions/package.json +++ b/examples/openapi-subscriptions/package.json @@ -9,7 +9,7 @@ "graphql-sse": "^2.5.3", "tslib": "^2.8.1", "url-join": "^5.0.0", - "@graphql-hive/gateway": "^1.9.0" + "@graphql-hive/gateway": "^1.9.1" }, "devDependencies": { "tsx": "^4.19.2" diff --git a/examples/operation-field-permissions/example.tar.gz b/examples/operation-field-permissions/example.tar.gz index f1ad0d802e93ad7c2049021fa2efe09f8a46000a..054c0fe5fd77c94bfd955985417bf0a6337a7dca 100644 GIT binary patch delta 1528 zcmViw0+&6oxfHC2*)irLJdQ6o zNklutR!OnLk~#YYRl%G)QJ80Q-8q}QwGL+8ptn<1@t4ELLuyUNl_P@+H8uXP#}vvC zjw*C9Nz%9v`I6Zv1HXaOlSbe}f7rdThB~^%krNTGcQ8Aj2Qk@0Gs;O#7kMy!Bxt*x#<#=J1tyZ*5u`$*2 z9Y&zabf>O%Yf5A`U|QGfT5m;1#zrfAL|?YuMgw zTFp=>Q&wsvVycyu=T?tl%jHJ*shSgL60$zU@;D)D{hYAL_l3N&YOD%Uh)(cXvTo2+ zhNNoUMn;iKyta7F7*+a0aX(%X4bdeOJXmM%aEtVYBIycbt^{cKNR+l4a~?k$hrIDP z=gFrz^oZBwqm3P`HO*6mfAtAmzhE%;Bj;?_*q+@h?BFi#dEV=Hb9M)o?S(Aj(}n%K zH`CcZlO0X~pAz%4K`BiF>UC9W>N&cmHux9;Izw$RC}pBTxYE=6f}(QNI3!1kyp%2( zlj25Sy+nI#FBK|u4L(9t(pRzD<-2c#jf+#$_zINUfnE+6)Q=g|e*@hXFs8o*V|wJv z2Mp;?#gML5NM7Kk?foO)Bz)G<+fKe{Ro*_CBG*1}nAV%>3tfTi0!yeufu~pF#&TVY z0|8`Kx>%{k<&}{m=7e1Dy|Q50FzTc%R%V@IQ*4bp-->YJd*%%VpNz&Wn}x9$l)!*}~ZdpDJ;C z$nT^YYurqDm2es80y0JcaTmaO$s#UI8{-Zge``mzLTF3KeLovPTmWJW+q2{xzX%D7 z@_DnHwm7&O?5LjnZEO0t2`tA2I}wqc2DrW?4J6r%f0DqInEg^G%(sto-Km(}A`}m` z5VxN(T*Y()_KaEYN2g^@L$~!u?ZNHmofNkgLiT+BQ}f@g--Tn$oZ}30bpF4)Cu1Z; z7g+4Va(%%sKL34ve`kE2S;hzWpA1L#@jp#7RDl0~1c)-SC4+s(cnv^=(MI-UH0%vS zUR|*}e?-IIeYx3>#0x;?ez%{V)iLKLa4_l`zJv*Hc5}Ul=x3Yc-olJBr%%B!XZoB; zVk1t{Z4E=+EF|eE@()f~f4z5zPefQ}!3ZWJK4S$`aI=DaCa`ZVaf)JKO#^6a&$*Y) zk(k524$*oNORK5`IOVsUNHR!bT{!Ny@7}!mW}-k(wrCYiVr_H(=Y6Vg4jFCpNT|8n z)Mh1x0sHgoZXa!HY6abYw<~eSs)BNdy*+H>xn#V3V}+srhevc$1eE(IoQn@% e0(w1w00IagfB*srAb} zran*0axGC?EhFp6hUslUJ)zb&kqsM;Hm;#+x@YL^S3+w#V>ITXNQsJBJs2I&hsm_98)l@a4M|#HxXH6=0|FfH|I6+QN8JRK5d&1vJK-cOiE<_w{lS*1BC@QPcNfB10cHEeG- zt!5~cDJ!)SG1bb-bF0U&<#MC@RLu!A30a?Fd7O~7eoolr`$Aq>HC6>FL?`$xSvP1Z zLsGSFBcsSAUR%6oj4FMhx*sozhUgLs9;~x>xJ7zXk#+@gR{}JABuZPsoF|UPp=dnL zdGcuvJ>qrwXk!QKo91c4e|iOOTrgPsk#n|dh-dd2J1C_+FM9oM&F;doqmV^>hOnRa zW;)wvvcn1BQ(}HLD5Xh2y{<}4JxABn1|K6pXQ&MZrA$-^S9*G1P*jc@hvZ0+m(nF; zQrzgPmuQder9y?S!AFQn`YL|Ae9vtlxHz?quRysSnB{;${g^>Le=uzUWBN-lrbpg< zz>xk_4Cz{h{oT-3SC0Yfke2*K?b*3oSUasP{ z@e0Qk=lZK!8^W;Zob`zg0^5sFD7(MZkxyF`X#-Ug)JrSbgf=r2BVos?_S$ZlSHDdB> zCqLi@Kq9s=RMEjE8T0ypw8Oqj>d^8_YMy5y>uwKO>`jL3e+l_M6?ad}-epiYMdc+` zu{sg%BpLZ#SO;_;~>>49;}vJ`#a6#Nn+h z>S%-O#Ej#o+!#DgbXB>ejW7h7rp+sa{cD(^DfhSeoQ%;F^9c)IYmleF(C3_nVLzg& zpApbbb`==@e*!B`o=Jc>u_{cYachTeyWQG^9LAHdN@W2gbw;2Nk1kc=h;X*ar&`<| z@;jx*8aESOC0qu&fSgf4+y!u6vWQF5$GAhs-`ZKN6xvd9->*gx7l0VU@hmwfE<(Z* z3%}VzTO5=IJEkXp+m=3V0V{C9PDbRX0e)YS2GZpA1L#@jp#7RDl0~1c);7C4+s(cnv^=(MJAcH0%vS zQC+b+e?-IIeYx4s#0x;?ez)(Q)iLK5a4_l`K7<8tc5A(d=x1Bx-olJBr%%B!XU3dK zVk1t{Z3{!)Y$WMv@()f~f4z5zPefQ}!3Y*3K4S&caI=DaCa`ZVaf)(aO#^6a&$*Y) zk(kTA4$*oNORK5`IOVsUOfpDfUAXSI&)&TCW}-w-wdgCF#M-9(=Y6Sf4jFCpNNBj* z(q<=xpis)BNdy*+I6xn#Wk!%9Q{A0E+35m4@6i=mUf31F)s=f3@_q4FqA%{EAUj$+najCxoO8qJpXydMJp{V}T`EuvI@& z+X;2GcV|3)?0O+lf0UQhiaR{y%3(1TxDx4#PaY?x}){g0l0f!&nU-#DzMB~^;~!A$PmqcIPn_?+wqeMVmxyJj2jau%~q zuFT|~l0kp(wMIzmf4^Yd92kR;56mIF9GnC$(i){q`w$hiSIg`}D}ii^(Mjp-7Pc0q zSGtoeEwf+aXy_pGbpLPm|5C{p8Dq-F`~4^Ve{H+H-|zQ!;NV4S--7?#|F`tN9;DB# zV@v$+bb8&D{>PMNpX2{)Ac$zODtLrZDlvKtX~C)#7I2p)e-T)eq*{l&D?}H`G?!@; zE~LttWfLwji7S~XOe53o2+MMq(mVz@Qj=jGAs7#!-2^(}48Z!5_%VD=q^0AhF78yt zB9?2#`^((J2s8L4g6=YZMex1UNlYfFQQa8ZaZ}o`DR4(J_Y;(4ou3UaEs{+0s&Ic# z8FPI*zYfNke=4BFQP5MOQ=(VJpXR5gP%n;=6=wsweJr!QN)IW!8mP8&&(ueN>@shx zKZnA)4An`Rin*;ezpAAPY{#jR)cOP*$b&qjHuzSkbfIgvDl)@r_EXcDS*2gc^Zr0R zWSoZivWm50*eqdPPGPJOR2bM?BhkI9h=%HJ(mK&%fAzp}gIKEsO9K9)Np+Jw(WdH0 zWb=3gzKF*mT7yHtF-Gf=c$IOBeA+cJPM~{nW>{F!7;*9nNQQW^kH3b(H};#F-CEqHMC(oNX3IA=z3n8 zj@4p^J&n%gxb$#M#x{{P!B)zUY+_DA>4wR@eRFbtT4Y0@>9H>%iGiXHUKxkUNQ8cZ i9`o*z1TAi`e$wGz$4rym^BW9|w}jsUV)rWmI0OJ5GqaQc delta 857 zcmV-f1E&0g=mUi41F)s=f7Si84FhqW`72Ju!n&tdmLa|L!#xQ zOVY9m^?%=;?R@kDbrXmo?S2n!65pNgY~S_b)XqeVeu#VN(i+d_*Wn?0y`K6;tbTj_ zZdsHgpB|D}>IGRBmT_xn%!|JreTzu)iez`={uz6Jle|8MDkJxHHf z$Cmit>GZlQ{f{ZlKF9ypKoHSpRqzO-RATfP(uP$jEZ{CpeX)eYkgk(Q31y0}vn zi&(A|?=N!?Bh28J2)fJs6~XsXCo!3zMs;Is$4%+LrobJ^+)q%Fb$&Lyv`8|otHSd= zWz6;M{5lwCf2x2IM?p`CPKjO>f100~LcKXgR{R>!^s&tHDm|p^YM|QAJyS0Mvdg@& z{u~PHGE^sND(1F2{HnGlupOsLQtJzFAP@48+TdHE(uJlqhIZsVzW|e*&&-(-Q zkZ~I3%PQ82VY7sFIfb!CP+?$mjYRjZA{wgMq;;ajf9i$h2C-HNmIVAoljBG9>qCbpeV~pZJYv{mukctOK(Dl4F zU8}_oJ{z6Oap~cjjBO%of~}Mx*~FZL(hZY)`{v~Qw8(})(_>#k5(7mYyfY4ykqG?+ jJ?7mb30mA>{iMUcj+qRT-18d@ZwbEv{|P=M05}8yQV+69 diff --git a/examples/programmatic-batching/package-lock.json b/examples/programmatic-batching/package-lock.json index 78cbbf056..a7a823075 100644 --- a/examples/programmatic-batching/package-lock.json +++ b/examples/programmatic-batching/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "@example/programmatic-batching", "dependencies": { - "@graphql-hive/gateway": "^1.9.0", + "@graphql-hive/gateway": "^1.9.1", "@graphql-mesh/compose-cli": "^1.2.13", "@omnigraph/openapi": "^0.108.6", "fets": "^0.8.4", @@ -1060,9 +1060,9 @@ } }, "node_modules/@graphql-hive/gateway": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@graphql-hive/gateway/-/gateway-1.9.0.tgz", - "integrity": "sha512-baTF35N3yFLLOx0+cFgencZ6cPLz18S38V6k6m/N34lOW356obYsarDrhecndYqJSeecuosAxs3CoVA+jAJnag==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@graphql-hive/gateway/-/gateway-1.9.1.tgz", + "integrity": "sha512-MyML8yKqYN9e5FRmsEpQ2R4Fd0jgpMX+0KEtd/I3UIDhtqVXAFHQ89F9b7A7LmV0OPCV9hpM2wLFBHwrfTXF7A==", "license": "MIT", "dependencies": { "@commander-js/extra-typings": "^13.0.0", diff --git a/examples/programmatic-batching/package.json b/examples/programmatic-batching/package.json index 72d4a6ab6..b258e80e7 100644 --- a/examples/programmatic-batching/package.json +++ b/examples/programmatic-batching/package.json @@ -7,7 +7,7 @@ "fets": "^0.8.4", "graphql": "16.10.0", "tslib": "^2.8.1", - "@graphql-hive/gateway": "^1.9.0" + "@graphql-hive/gateway": "^1.9.1" }, "devDependencies": { "tsx": "^4.19.2" diff --git a/examples/subscriptions-with-transforms/example.tar.gz b/examples/subscriptions-with-transforms/example.tar.gz index 0a7c07eecc3cea4b5ef4660ebd4af8d0c3c143aa..148f137113ed1b8db7f5616c5361339d8d43ded6 100644 GIT binary patch delta 1856 zcmV-G2fz6C%LDbx1F+5Be@EJ!whruPeuZA9Ga-*SJ^%yB^vxp>?f~IR(hsjSMqm>k z#Im_M{P!o>25bt1QYP(8y)%hzw7OQS)vk^e*Ddj?l(NKWPtmI50t+LC{`^u_tEuXA z!p(;BL}w0bDZMu5<|QUpKgCwljf38Y&d}P<7%fv)RvN6&*s;t$f0d-hf+CZdVJD~f z_MJJK8Qnuu*pr!Wa(%frdFCw4u7Vc(igH*AZ?n{lO=>%sic~fE!#qV2#9T!lwn~~j zV1LP|pPS?ElYJ}|O2xF=sx<0cc2XGja&eRx>M7Hjo4to=S}ImEy>T-V)$+yaYrV%X z>5=qWYS2tYN>|%Wf1Zvcnwf4Y`CRKCH0^P?ZLx=fg5pl`Z{Hew%pQF=O+rWKjfx(& zKVOKo^AEyfy%0~SwWs#8mdkQ+qOEbXq^IvAi0fq2YBNHQ>ef0a05Qq4}%CJKI1P9g`m z^XfI{^TtCYk|}G|BK=~NwdY1BQ7V_~?Wg-YiD$9tQz#Eos_}Ry&2x`ZPMg-Jc_qT9 z#4KC0_-KZWR@?QArWQqG{F<@u`JdeGCn~ZfuM`EJ)Y(|vV*R0HcLi}*0u8D!E9;3l z-#r@JZsS?5e<$y^&^f=$M^`#HW124urwLZ~g5lJ6th1eBdUhkTt+TYp-CpladBs@n zEo9t*SJ<1K5h^_pgX>**TpyuTRo%OEoNd8Q|X{bNz;Ys zAfj>wQMoNz!@?X*KgT+yPCAlrTOvnSZcpO3>tNaje-jt`y737#w=Gl-4Ai#`)GcHS z4AVctFx}DefkFCP8Kg5Es~42*dw)lpFh?!DdD11L^7_l(a(#ddGdeG5j5KU6lxSVb zi~Mv@pUkRBD8bAW$(8%0I<@X-lU8e;*Akr6ts1M!l~JqkAU6iBa;5^OmDoK@=Q_f` zM2Ui0fBk$Cw~Z%Qt~l0T)y&#`@+OLv!y~jOiyXbd|0y?x1NI4UImmXk2&^D8pUgHI ze6A&o^VtZd^<>spbxSIsVtXK@v-9L|oGW*0bpJ`@Oruo(Jcz{aZiiBAkXNH}n>M4m zR#u)0I}b4(dQQG8bfH48?@%QRn{3?g1Jb?re_hdgj$cxHJ&T;~_Nm4GU~HbS--lAG z6|?UUG(povMc14{Fg|C!WR_v+QX5W3#!B_~yQ*BD9oclwezesaB7cj}hm!)bx{h0L z%!3FIvv}|^4J^3&x}hR^B#1A<0A3A|t{tBTgMsIH`-x%m$udlQ`R@GA@?0$O0Whbl zf97tmkSQ1dyhm2QBM{bGt+@XSr-C|CAib9>Y(778ZyP&XA7j|wwa@+?7{W+;RVWoA ziMTqzJp)qM^0xbi?ZJAfC7!vc_S^*T?4b6CPzWuJ?fBJZG-{>~1b3*#aaWSmw_k33 zsAgRW&QQH@D>mLbe|@<;EAgLuG-VaRA)W`3I2OPH@mifxn6`Gl zBqELv+#p1I!e~6C!0DBVmPK{RM&b}MQtmgPbbu@@jOEJxx(*F{h`X9%}qfDUxy$8rDaef580RsxXoUFj+OcTKZkZXv+ zIk85r`{?{8w89fHq6wSCQIg@uBNnO`bEwOjAA_8!^I{-qwy>$ETP6^Y=j)?C8Zig4 zo$c<0-&Pt?f~s)oem!F?aFu{pe>KDrLo^NQhCY^i5X^MT0LYFSBA3ybfFOZSo@ByA zeREK*eR+i)y^a{8S=dL{KJtZ0&BI^5^kjo*)|cS?igprNYG~&d_(yv&8Nb;nxu(8> z*4E&I1&x{4IFz8G=+_{80Y3c$&L_H|^v*9g&B~^yLwh5OI<~8O`b6_De_CUDv?LjU ziL5BN5T<2lz?2}cPaptY0}vN#23XuWA|RHz0%Via7S{&>#*>nO2I`4-fK|Q%)c(2$H`}?0tg_000IagfB*tGCHxQV3YEzK=mP-xT#|VJ delta 1857 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 delta 758 zcmVBLH4*wA+XVv_+#)TL-mWzDm$ zC@nC9(Jamg527?z?i4*qdR~RsLmkj2=P(@RWWe-;fCLl`i6OnD!6gTmY_m&@IS?@Y zjuhvHG+E#Ot;`&yjp0rmkHE;tLJBVMnDgTIGcORsLV>D(OXFBC7e}d0IkNZFt4wMuKeFAx0f<^^=3W0Ct@C(O-h6d%CW1wb~$%nI6 z%znH~fwUgriB}K+!uz$DeL^5#4VWiuSmz{Q#5pLKs4c%y@Rq(F^3g(p|9>+$W6l<2 z#U4=VxlXNQI|Y1yU!nTs`e1PhuK!r(HaI|28K2MaIU<4xfAISH8o^&)c}r;HQVe7k z04{>!@B#2A4hahM*rzkjSljWWaUuVq3>ogyFkpCn}#k}%AN}R_3@8aG?{_Cx1v-DRv zmB%8zBFRty#j&CZW{lByi?;aRq!gAYviLw9XmKh?3s7Cf68?A9#XYC8H<%eKdc%sb zQedVzz;L81&ZAmz{KqF-FRZE-i^LWkqa)sd4aaRkY++V~TTeMF!3I81dszipQDZ*y z_m!(aD`19HGgJ%yZ3V7XD?K-C+;Q#;t diff --git a/examples/type-merging-batching/package-lock.json b/examples/type-merging-batching/package-lock.json index 567c8e37b..d5f573cab 100644 --- a/examples/type-merging-batching/package-lock.json +++ b/examples/type-merging-batching/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "@example/type-merging-batching", "dependencies": { - "@graphql-hive/gateway": "^1.9.0", + "@graphql-hive/gateway": "^1.9.1", "@graphql-mesh/compose-cli": "^1.2.13", "graphql": "^16.9.0", "graphql-yoga": "^5.10.11", @@ -1059,9 +1059,9 @@ } }, "node_modules/@graphql-hive/gateway": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@graphql-hive/gateway/-/gateway-1.9.0.tgz", - "integrity": "sha512-baTF35N3yFLLOx0+cFgencZ6cPLz18S38V6k6m/N34lOW356obYsarDrhecndYqJSeecuosAxs3CoVA+jAJnag==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@graphql-hive/gateway/-/gateway-1.9.1.tgz", + "integrity": "sha512-MyML8yKqYN9e5FRmsEpQ2R4Fd0jgpMX+0KEtd/I3UIDhtqVXAFHQ89F9b7A7LmV0OPCV9hpM2wLFBHwrfTXF7A==", "license": "MIT", "dependencies": { "@commander-js/extra-typings": "^13.0.0", diff --git a/examples/type-merging-batching/package.json b/examples/type-merging-batching/package.json index c468dcc4c..b9843c4f0 100644 --- a/examples/type-merging-batching/package.json +++ b/examples/type-merging-batching/package.json @@ -6,7 +6,7 @@ "graphql": "^16.9.0", "graphql-yoga": "^5.10.11", "tslib": "^2.8.1", - "@graphql-hive/gateway": "^1.9.0" + "@graphql-hive/gateway": "^1.9.1" }, "devDependencies": { "tsx": "^4.19.2" diff --git a/package.json b/package.json index c9c9364f5..e9fbc2c55 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "vitest": "^3.0.1" }, "resolutions": { + "@envelop/core": "5.1.0-alpha-20250206123608-cd323a08c43c066eb34cba698d61b4f059ab5ee5", "@graphql-tools/delegate": "workspace:^", "@opentelemetry/exporter-trace-otlp-http": "patch:@opentelemetry/exporter-trace-otlp-http@npm%3A0.56.0#~/.yarn/patches/@opentelemetry-exporter-trace-otlp-http-npm-0.56.0-dddd282e41.patch", "@opentelemetry/otlp-exporter-base": "patch:@opentelemetry/otlp-exporter-base@npm%3A0.56.0#~/.yarn/patches/@opentelemetry-otlp-exporter-base-npm-0.56.0-ba3dc5f5c5.patch", diff --git a/packages/plugins/opentelemetry/package.json b/packages/plugins/opentelemetry/package.json index 0cdfcddcb..bfd55bcd0 100644 --- a/packages/plugins/opentelemetry/package.json +++ b/packages/plugins/opentelemetry/package.json @@ -50,6 +50,7 @@ "@graphql-mesh/utils": "^0.103.6", "@graphql-tools/utils": "^10.7.0", "@opentelemetry/api": "^1.9.0", + "@opentelemetry/context-async-hooks": "^1.30.1", "@opentelemetry/exporter-trace-otlp-grpc": "^0.57.0", "@opentelemetry/exporter-trace-otlp-http": "^0.57.0", "@opentelemetry/exporter-zipkin": "^1.29.0", @@ -62,6 +63,7 @@ "tslib": "^2.8.1" }, "devDependencies": { + "@whatwg-node/server": "^0.9.65", "graphql": "^16.9.0", "graphql-yoga": "^5.10.11", "pkgroll": "2.6.1" diff --git a/packages/plugins/opentelemetry/src/contextManager.ts b/packages/plugins/opentelemetry/src/contextManager.ts new file mode 100644 index 000000000..f4bae5c2b --- /dev/null +++ b/packages/plugins/opentelemetry/src/contextManager.ts @@ -0,0 +1,42 @@ +import { trace, type Context } from '@opentelemetry/api'; + +type Node = { + ctx: Context; + previous?: Node; +}; + +export class OtelContextStack { + #root: Node; + #current: Node; + + constructor(root: Context) { + this.#root = { ctx: root }; + this.#current = this.#root; + } + + get current(): Context { + return this.#current.ctx; + } + + get root(): Context { + return this.#root.ctx; + } + + push = (ctx: Context) => { + this.#current = { ctx, previous: this.#current }; + }; + + pop = () => { + this.#current = this.#current.previous ?? this.#root; + }; + + toString() { + let node: Node | undefined = this.#current; + const names = []; + while (node != undefined) { + names.push((trace.getSpan(node.ctx) as unknown as { name: string }).name); + node = node.previous; + } + return names.join(' -> '); + } +} diff --git a/packages/plugins/opentelemetry/src/plugin-utils.ts b/packages/plugins/opentelemetry/src/plugin-utils.ts new file mode 100644 index 000000000..8a7a0bd12 --- /dev/null +++ b/packages/plugins/opentelemetry/src/plugin-utils.ts @@ -0,0 +1,104 @@ +import type { ExecutionRequest } from '@graphql-tools/utils'; + +export function withState< + P, + GraphqlState = object, + HttpState = object, + SubExecState = object, +>(plugin: WithState): P { + const states: { + forRequest?: WeakMap>; + forOperation?: WeakMap>; + forSubgraphExecution?: WeakMap>; + } = {}; + + function getProp(scope: keyof typeof states, key: any): PropertyDescriptor { + return { + get() { + if (!states[scope]) states[scope] = new WeakMap(); + let value = states[scope].get(key as any); + if (!value) states[scope].set(key, (value = {})); + return value; + }, + enumerable: true, + }; + } + + const pluginWithState: Record unknown> = {}; + for (const [hookName, hook] of Object.entries(plugin) as any) { + pluginWithState[hookName] = (payload) => + hook({ + ...payload, + get state() { + let { executionRequest, context, request } = payload; + + const state = {}; + const defineState = (scope: keyof typeof states, key: any) => + Object.defineProperty(state, scope, getProp(scope, key)); + + if (executionRequest) { + defineState('forSubgraphExecution', executionRequest); + if (executionRequest.context) context = executionRequest.context; + } + if (context) { + defineState('forOperation', context); + if (context.request) request = context.request; + } + if (request) { + defineState('forRequest', request); + } + return state; + }, + }); + } + + return pluginWithState as P; +} + +export type HttpState = { + forRequest: Partial; +}; + +export type GraphQLState = { + forOperation: Partial; +}; + +export type GatewayState = { + forSubgraphExecution: Partial; +}; + +export function getMostSpecificState( + state: Partial & GraphQLState & GatewayState> = {}, +): Partial | undefined { + const { forOperation, forRequest, forSubgraphExecution } = state; + return forSubgraphExecution ?? forOperation ?? forRequest; +} + +// Brace yourself! TS Wizardry is coming! + +type PayloadWithState = T extends { + executionRequest: any; +} + ? T & { + state: Partial & GraphQLState> & + GatewayState; + } + : T extends { + executionRequest?: any; + } + ? T & { + state: Partial< + HttpState & GraphQLState & GatewayState + >; + } + : T extends { context: any } + ? T & { state: HttpState & GraphQLState } + : T extends { request: any } + ? T & { state: HttpState } + : T; + +type WithState = { + [K in keyof P]: P[K] extends ((payload: infer T) => infer R) | undefined + ? (payload: PayloadWithState) => R | undefined + : P[K]; +}; diff --git a/packages/plugins/opentelemetry/src/plugin.ts b/packages/plugins/opentelemetry/src/plugin.ts index cce3fe667..099dcc536 100644 --- a/packages/plugins/opentelemetry/src/plugin.ts +++ b/packages/plugins/opentelemetry/src/plugin.ts @@ -1,52 +1,75 @@ import { + type OnContextBuildingEventPayload, type OnExecuteEventPayload, type OnParseEventPayload, type OnValidateEventPayload, } from '@envelop/types'; import { type GatewayPlugin } from '@graphql-hive/gateway-runtime'; import type { OnSubgraphExecutePayload } from '@graphql-mesh/fusion-runtime'; -import type { Logger, OnFetchHookPayload } from '@graphql-mesh/types'; +import type { + Logger, + OnFetchHookDone, + OnFetchHookPayload, +} from '@graphql-mesh/types'; import { getHeadersObj } from '@graphql-mesh/utils'; -import { - fakePromise, - isAsyncIterable, - MaybePromise, -} from '@graphql-tools/utils'; +import { createDeferred, isPromise, MaybePromise } from '@graphql-tools/utils'; import { context, diag, DiagLogLevel, propagation, + ROOT_CONTEXT, trace, type Context, + type ContextManager, type TextMapGetter, type Tracer, } from '@opentelemetry/api'; +import '@opentelemetry/api'; import { Resource } from '@opentelemetry/resources'; import { type SpanProcessor } from '@opentelemetry/sdk-trace-base'; import { WebTracerProvider } from '@opentelemetry/sdk-trace-web'; import { DisposableSymbols } from '@whatwg-node/disposablestack'; import { type OnRequestEventPayload } from '@whatwg-node/server'; +import type { OnParamsEventPayload, YogaInitialContext } from 'graphql-yoga'; import { ATTR_SERVICE_VERSION, SEMRESATTRS_SERVICE_NAME } from './attributes'; +import { OtelContextStack } from './contextManager'; +import { + getMostSpecificState, + withState, + type GatewayState, + type GraphQLState, + type HttpState, +} from './plugin-utils'; import { - completeHttpSpan, - createGraphQLExecuteSpan, - createGraphQLParseSpan, - createGraphQLValidateSpan, + addExecutionArgsToGraphqlSpan, + createGraphqlContextBuildingSpan, createHttpSpan, - createSubgraphExecuteFetchSpan, - createUpstreamHttpFetchSpan, + endHttpSpan, + startGraphQLExecuteSpan, + startGraphQLParseSpan, + startGraphQLSpan, + startGraphQLValidateSpan, + startSubgraphExecuteFetchSpan, + startUpstreamHttpFetchSpan, } from './spans'; +import { mapMaybePromise } from './utils'; -type PrimitiveOrEvaluated = - | TExpectedResult - | ((input: TInput) => TExpectedResult); +type BooleanOrPredicate = + | boolean + | ((input: TInput) => boolean); interface OpenTelemetryGatewayPluginOptionsWithoutInit { /** * Whether to initialize the OpenTelemetry SDK (default: true). */ initializeNodeSDK: false; + /** + * Whether to rely on OTEL context api for span correlation (default: true). + * If false, the plugin will rely on request context for span parenting, + * which implies that any user defined context and spans will be ignored. + */ + contextManager?: false; } interface OpenTelemetryGatewayPluginOptionsWithInit { @@ -67,6 +90,13 @@ interface OpenTelemetryGatewayPluginOptionsWithInit { * Does not apply when `initializeNodeSDK` is `false`. */ serviceName?: string; + /** + * The context manager used to keep track of the OTEL context. + * By default, it uses AsyncLocalStorage based manager, which is compatible only in Node. + * + * Does not apply when `initializeNodeSDK` is `false`. + */ + contextManager?: ContextManager | false; } type OpenTelemetryGatewayPluginOptionsInit = @@ -107,36 +137,37 @@ export type OpenTelemetryGatewayPluginOptions = * * Disabling the HTTP span will also disable all other child spans. */ - http?: PrimitiveOrEvaluated>; + http?: BooleanOrPredicate>; + /** + * Enable/disable GraphQL operation spans (default: true). + */ + graphql?: BooleanOrPredicate>; + /** + * Enable/disable GraphQL context building phase (default: true). + */ + graphqlContextBuilding?: BooleanOrPredicate< + OnContextBuildingEventPayload + >; /** * Enable/disable GraphQL parse spans (default: true). */ - graphqlParse?: PrimitiveOrEvaluated>; + graphqlParse?: BooleanOrPredicate>; /** * Enable/disable GraphQL validate spans (default: true). */ - graphqlValidate?: PrimitiveOrEvaluated< - boolean, - OnValidateEventPayload - >; + graphqlValidate?: BooleanOrPredicate>; /** * Enable/disable GraphQL execute spans (default: true). */ - graphqlExecute?: PrimitiveOrEvaluated< - boolean, - OnExecuteEventPayload - >; + graphqlExecute?: BooleanOrPredicate>; /** * Enable/disable subgraph execute spans (default: true). */ - subgraphExecute?: PrimitiveOrEvaluated< - boolean, - OnSubgraphExecutePayload - >; + subgraphExecute?: BooleanOrPredicate>; /** * Enable/disable upstream HTTP fetch calls spans (default: true). */ - upstreamFetch?: PrimitiveOrEvaluated>; + upstreamFetch?: BooleanOrPredicate>; }; }; @@ -149,262 +180,374 @@ const HeadersTextMapGetter: TextMapGetter = { }, }; -export function useOpenTelemetry( - options: OpenTelemetryGatewayPluginOptions & { logger: Logger }, -): GatewayPlugin<{ +export type OpenTelemetryContextExtension = { opentelemetry: { tracer: Tracer; activeContext: () => Context; }; -}> { +}; + +type OtelState = { + otel: OtelContextStack; +}; + +type State = Partial< + HttpState & GraphQLState & GatewayState +>; + +export function useOpenTelemetry( + options: OpenTelemetryGatewayPluginOptions & { logger: Logger }, +): GatewayPlugin { const inheritContext = options.inheritContext ?? true; const propagateContext = options.propagateContext ?? true; + const useContextManager = options.contextManager !== false; - const requestContextMapping = new WeakMap(); let tracer: Tracer; let spanProcessors: SpanProcessor[]; - let serviceName: string = 'Gateway'; let provider: WebTracerProvider; - let preparation$: Promise | undefined; - - return { - onYogaInit({ yoga }) { - preparation$ = fakePromise(undefined).then(async () => { - if ( - !( - 'initializeNodeSDK' in options && - options.initializeNodeSDK === false - ) - ) { - if (options.serviceName) { - serviceName = options.serviceName; - } - if (options.exporters) { - spanProcessors = await Promise.all(options.exporters); - } - const webProvider = new WebTracerProvider({ - resource: new Resource({ - [SEMRESATTRS_SERVICE_NAME]: serviceName, - [ATTR_SERVICE_VERSION]: yoga.version, - }), - spanProcessors, - }); - webProvider.register(); - provider = webProvider; - } - const pluginLogger = options.logger.child('OpenTelemetry'); - diag.setLogger( - { - error: (message, ...args) => pluginLogger.error(message, ...args), - warn: (message, ...args) => pluginLogger.warn(message, ...args), - info: (message, ...args) => pluginLogger.info(message, ...args), - debug: (message, ...args) => pluginLogger.debug(message, ...args), - verbose: (message, ...args) => pluginLogger.debug(message, ...args), - }, - DiagLogLevel.VERBOSE, - ); + let preparation$: Promise | undefined | void; + const { promise: asyncAttributes, resolve: resolveAsyncAttributes } = + createDeferred<{ [ATTR_SERVICE_VERSION]: string }>(); + + function isParentEnabled(state: State): boolean { + const parentState = getMostSpecificState(state); + return !parentState || !!parentState.otel; + } + + function getContext(state?: State): Context { + return useContextManager + ? context.active() + : (getMostSpecificState(state)?.otel?.current ?? ROOT_CONTEXT); + } + + const pluginLogger = options.logger.child('OpenTelemetry'); + diag.setLogger( + { + error: (message, ...args) => pluginLogger.error(message, ...args), + warn: (message, ...args) => pluginLogger.warn(message, ...args), + info: (message, ...args) => pluginLogger.info(message, ...args), + debug: (message, ...args) => pluginLogger.debug(message, ...args), + verbose: (message, ...args) => pluginLogger.debug(message, ...args), + }, + DiagLogLevel.VERBOSE, + ); + + if ( + !('initializeNodeSDK' in options && options.initializeNodeSDK === false) + ) { + const exporters$ = containsOnlyValues(options.exporters) + ? options.exporters + : Promise.all(options.exporters); + + const resource = new Resource( + { [SEMRESATTRS_SERVICE_NAME]: options.serviceName || 'Gateway' }, + asyncAttributes, + ); + + const contextManager$ = + options.contextManager != undefined + ? options.contextManager + : import('@opentelemetry/context-async-hooks').then( + (module) => new module.AsyncLocalStorageContextManager(), + ); + + preparation$ = mapMaybePromise(exporters$, (exporters) => { + spanProcessors = exporters; + provider = new WebTracerProvider({ resource, spanProcessors }); + return mapMaybePromise(contextManager$, (contextManager) => { + provider.register({ + contextManager: contextManager === false ? undefined : contextManager, + }); tracer = options.tracer || trace.getTracer('gateway'); preparation$ = undefined; }); + }); + } else { + tracer = options.tracer || trace.getTracer('gateway'); + } + + return withState< + GatewayPlugin, + OtelState, + OtelState, + OtelState + >({ + onYogaInit({ yoga }) { + resolveAsyncAttributes({ [ATTR_SERVICE_VERSION]: yoga.version }); }, - onContextBuilding({ extendContext, context }) { - extendContext({ - opentelemetry: { - tracer, - activeContext: () => - requestContextMapping.get(context.request) ?? context['active'](), + onRequest(onRequestPayload) { + if (!shouldTrace(options.spans?.http, onRequestPayload)) { + return; + } + + return mapMaybePromise( + preparation$, + () => { + const { requestHandler, request, setRequestHandler, state } = + onRequestPayload; + + const { ctx } = createHttpSpan({ + ctx: inheritContext + ? propagation.extract( + context.active(), + request.headers, + HeadersTextMapGetter, + ) + : context.active(), + request, + tracer, + url: onRequestPayload.url, + }); + + if (useContextManager) { + setRequestHandler(context.bind(ctx, requestHandler)); + } + + state.forRequest.otel = new OtelContextStack(ctx); }, - }); + (error) => { + pluginLogger.error('Failed to start http span', { error }); + }, + ); }, - onRequest(onRequestPayload) { - const shouldTraceHttp = - typeof options.spans?.http === 'function' - ? options.spans.http(onRequestPayload) - : (options.spans?.http ?? true); + onResponse({ response, state }) { + try { + state.forRequest.otel && + endHttpSpan(state.forRequest.otel.root, response); + } catch (error) { + pluginLogger.error('Failed to end http span', { error }); + } + }, + onParams(onParamsPayload) { + const { setParamsHandler, paramsHandler, params, state } = + onParamsPayload; + const { forOperation, ...parentState } = state; - if (!shouldTraceHttp) { - return preparation$; + if (!isParentEnabled(parentState)) { + return; } - const { request, url } = onRequestPayload; - const otelContext = inheritContext - ? propagation.extract( - context.active(), - request.headers, - HeadersTextMapGetter, - ) - : context.active(); - - const httpSpan = createHttpSpan({ - request, - url, - tracer, - otelContext, - }); + if (shouldTrace(options.spans?.graphql, onParamsPayload)) { + const { ctx, done } = startGraphQLSpan({ + tracer, + params, + ctx: getContext(parentState), + }); - requestContextMapping.set(request, trace.setSpan(otelContext, httpSpan)); + forOperation.otel! = new OtelContextStack(ctx); - return preparation$; - }, - onValidate(onValidatePayload) { - const shouldTraceValidate = - typeof options.spans?.graphqlValidate === 'function' - ? options.spans.graphqlValidate(onValidatePayload) - : (options.spans?.graphqlValidate ?? true); + const handler = useContextManager + ? context.bind(ctx, paramsHandler) + : paramsHandler; + + setParamsHandler((...args) => { + const result$ = handler(...args); + done(result$); + return result$; + }); + } - const { context } = onValidatePayload; - const otelContext = requestContextMapping.get(context.request); + const gqlCtx = onParamsPayload.context as YogaInitialContext & + OpenTelemetryContextExtension; + gqlCtx.opentelemetry = { + tracer, + activeContext: (): Context => getContext(state), + }; + }, + onContextBuilding(payload) { + const { state } = payload; + if (!isParentEnabled(state)) { + return; + } - if (shouldTraceValidate && otelContext) { - const { done } = createGraphQLValidateSpan({ - otelContext, + if (shouldTrace(options.spans?.graphqlContextBuilding, payload)) { + const { ctx, done } = createGraphqlContextBuildingSpan({ + ctx: getContext(state), tracer, - query: context.params.query, - operationName: context.params.operationName, }); - return ({ result }) => done(result); + state.forOperation.otel!.push(ctx); + + return () => { + done(); + state.forOperation.otel!.pop(); + }; } - return void 0; + + return; }, onParse(onParsePayload) { - const shouldTracePrase = - typeof options.spans?.graphqlParse === 'function' - ? options.spans.graphqlParse(onParsePayload) - : (options.spans?.graphqlParse ?? true); - - const { context } = onParsePayload; - const otelContext = requestContextMapping.get(context.request); + const { state } = onParsePayload; + if (!isParentEnabled(state)) { + return; + } - if (shouldTracePrase && otelContext) { - const { done } = createGraphQLParseSpan({ - otelContext, + if (shouldTrace(options.spans?.graphqlParse, onParsePayload)) { + const { context: gqlCtx, setParseFn, parseFn } = onParsePayload; + const { ctx, done } = startGraphQLParseSpan({ + ctx: getContext(state), tracer, - query: context.params.query, - operationName: context.params.operationName, + operationName: gqlCtx.params.operationName, + query: gqlCtx.params.query?.trim(), }); + state.forOperation.otel!.push(ctx); + if (useContextManager) { + setParseFn(context.bind(ctx, parseFn)); + } + return ({ result }) => { + done(result); + state.forOperation.otel!.pop(); + }; + } - return ({ result }) => done(result); + return; + }, + onValidate(onValidatePayload) { + const { context: gqlCtx, state } = onValidatePayload; + if (!isParentEnabled(state)) { + return; } - return void 0; + + if (shouldTrace(options.spans?.graphqlValidate, onValidatePayload)) { + const { setValidationFn, validateFn } = onValidatePayload; + const { ctx, done } = startGraphQLValidateSpan({ + ctx: getContext(state), + tracer, + query: gqlCtx.params.query?.trim(), + operationName: gqlCtx.params.operationName, + }); + state.forOperation.otel!.push(ctx); + if (useContextManager) { + setValidationFn(context.bind(ctx, validateFn)); + } + return ({ result }) => { + done(result); + state.forOperation.otel!.pop(); + }; + } + + return; }, onExecute(onExecuteArgs) { - const shouldTraceExecute = - typeof options.spans?.graphqlExecute === 'function' - ? options.spans.graphqlExecute(onExecuteArgs) - : (options.spans?.graphqlExecute ?? true); - - const { args } = onExecuteArgs; - const otelContext = requestContextMapping.get(args.contextValue.request); - - if (shouldTraceExecute && otelContext) { - const { done } = createGraphQLExecuteSpan({ - args, - otelContext, + const { state } = onExecuteArgs; + + if (!isParentEnabled(state)) { + return; + } + + addExecutionArgsToGraphqlSpan( + state.forOperation.otel!.root, + onExecuteArgs.args, + ); + + if (shouldTrace(options.spans?.graphqlExecute, onExecuteArgs)) { + const { setExecuteFn, executeFn } = onExecuteArgs; + const { ctx, done } = startGraphQLExecuteSpan({ + ctx: getContext(state), + args: onExecuteArgs.args, tracer, }); - + state.forOperation.otel!.push(ctx); + if (useContextManager) { + setExecuteFn(context.bind(ctx, executeFn)); + } return { - onExecuteDone: ({ result }) => { - if (!isAsyncIterable(result)) { - done(result); - } + onExecuteDone(payload) { + done(payload.result); + state.forOperation.otel!.pop(); }, }; } - return void 0; + + return; }, onSubgraphExecute(onSubgraphPayload) { - const shouldTraceSubgraphExecute = - typeof options.spans?.subgraphExecute === 'function' - ? options.spans.subgraphExecute(onSubgraphPayload) - : (options.spans?.subgraphExecute ?? true); - - const otelContext = onSubgraphPayload.executionRequest.context?.request - ? requestContextMapping.get( - onSubgraphPayload.executionRequest.context.request, - ) - : undefined; - - if (shouldTraceSubgraphExecute && otelContext) { - const { subgraphName, executionRequest } = onSubgraphPayload; - const { done } = createSubgraphExecuteFetchSpan({ - otelContext, + const { state } = onSubgraphPayload; + const { forSubgraphExecution, ...parentState } = state; + + if (!isParentEnabled(parentState)) { + return; + } + + // Here it is possible that otelCtx is not present, because this hook can be triggered by + // internal introspection queries, which are not linked to any client request, but should + // still be traced and monitored. + + if (shouldTrace(options.spans?.subgraphExecute, onSubgraphPayload)) { + const { subgraphName, executionRequest, executor, setExecutor } = + onSubgraphPayload; + const { ctx, done } = startSubgraphExecuteFetchSpan({ + ctx: getContext(parentState), tracer, executionRequest, subgraphName, }); - - return done; + forSubgraphExecution.otel = new OtelContextStack(ctx); + if (useContextManager) { + setExecutor(context.bind(ctx, executor)); + } + return ({ result }) => { + done(result); + forSubgraphExecution.otel!.pop(); + }; } - return void 0; + + return; }, onFetch(onFetchPayload) { - const shouldTraceFetch = - typeof options.spans?.upstreamFetch === 'function' - ? options.spans.upstreamFetch(onFetchPayload) - : (options.spans?.upstreamFetch ?? true); - - const { - context, - options: fetchOptions, - url, - setOptions, - executionRequest, - } = onFetchPayload; - - const otelContext = requestContextMapping.get(context.request); - if (shouldTraceFetch && otelContext) { - if (propagateContext) { - const reqHeaders = getHeadersObj(fetchOptions.headers || {}); - propagation.inject(otelContext, reqHeaders); - - setOptions({ - ...fetchOptions, - headers: reqHeaders, - }); - } + const { state, setFetchFn } = onFetchPayload; + if (!isParentEnabled(state)) { + return; + } + + // Here it is possible that otelCtx is not present, because this hook can be triggered by + // internal introspection queries, which are not linked to any client request, but should + // still be traced and monitored. + + let { fetchFn } = onFetchPayload; + const originalFetch = fetchFn; + let onDone: OnFetchHookDone | undefined = void 0; + + if (propagateContext) { + fetchFn = (url, options, ...args) => { + const reqHeaders = getHeadersObj(options?.headers || {}); + propagation.inject(getContext(state), reqHeaders); + return originalFetch( + url, + { ...options, headers: reqHeaders }, + ...args, + ); + }; + } + + if (shouldTrace(options.spans?.upstreamFetch, onFetchPayload)) { + const { url, options, executionRequest } = onFetchPayload; - const { done } = createUpstreamHttpFetchSpan({ - otelContext, + const { ctx, done } = startUpstreamHttpFetchSpan({ + ctx: getContext(state), tracer, url, - fetchOptions, + fetchOptions: options, executionRequest, }); - - return (fetchDonePayload) => done(fetchDonePayload.response); - } - return void 0; - }, - onResponse({ request, response }) { - const otelContext = requestContextMapping.get(request); - if (!otelContext) { - return; + state.forSubgraphExecution?.otel?.push(ctx); + if (useContextManager) { + fetchFn = context.bind(ctx, fetchFn); + } + onDone = ({ response }) => { + done(response); + state.forSubgraphExecution?.otel?.pop(); + }; } - const rootSpan = trace.getSpan(otelContext); + setFetchFn(fetchFn); - if (rootSpan) { - completeHttpSpan(rootSpan, response); - } - - requestContextMapping.delete(request); + return onDone; }, async [DisposableSymbols.asyncDispose]() { - if (spanProcessors) { - await Promise.all( - spanProcessors.map((processor) => processor.forceFlush()), - ); - } await provider?.forceFlush?.(); - - if (spanProcessors) { - spanProcessors.forEach((processor) => processor.shutdown()); - } - await provider?.shutdown?.(); diag.disable(); @@ -412,5 +555,24 @@ export function useOpenTelemetry( context.disable(); propagation.disable(); }, - }; + }); +} + +function containsOnlyValues( + maybePromises: MaybePromise[], +): maybePromises is T[] { + return !maybePromises.some(isPromise); +} + +function shouldTrace( + value: BooleanOrPredicate | null | undefined, + args: Args, +): boolean { + if (value == null) { + return true; + } + if (typeof value === 'function') { + return value(args); + } + return value; } diff --git a/packages/plugins/opentelemetry/src/spans.ts b/packages/plugins/opentelemetry/src/spans.ts index 11fc2afad..975d0ed77 100644 --- a/packages/plugins/opentelemetry/src/spans.ts +++ b/packages/plugins/opentelemetry/src/spans.ts @@ -1,17 +1,21 @@ import { defaultPrintFn } from '@graphql-mesh/transport-common'; import { getOperationASTFromDocument, + isAsyncIterable, + mapMaybePromise, type ExecutionRequest, type ExecutionResult, + type MaybePromise, } from '@graphql-tools/utils'; import { SpanKind, SpanStatusCode, + trace, type Context, - type Span, type Tracer, } from '@opentelemetry/api'; import type { ExecutionArgs } from 'graphql'; +import type { GraphQLParams } from 'graphql-yoga'; import { SEMATTRS_GATEWAY_UPSTREAM_SUBGRAPH_NAME, SEMATTRS_GRAPHQL_DOCUMENT, @@ -30,55 +34,144 @@ import { } from './attributes'; export function createHttpSpan(input: { + ctx: Context; tracer: Tracer; request: Request; url: URL; - otelContext: Context; -}): Span { - const { url, request, tracer, otelContext } = input; - const path = url.pathname; - const userAgent = request.headers.get('user-agent'); - const ips = request.headers.get('x-forwarded-for'); - const method = request.method || 'GET'; - const host = url.host || request.headers.get('host'); - const hostname = url.hostname || host || 'localhost'; - const rootSpanName = `${method} ${path}`; +}): { ctx: Context } { + const { url, request, tracer } = input; - return tracer.startSpan( - rootSpanName, + const span = tracer.startSpan( + `${request.method || 'GET'} ${url.pathname}`, { attributes: { - [SEMATTRS_HTTP_METHOD]: method, + [SEMATTRS_HTTP_METHOD]: request.method || 'GET', [SEMATTRS_HTTP_URL]: request.url, - [SEMATTRS_HTTP_ROUTE]: path, + [SEMATTRS_HTTP_ROUTE]: url.pathname, [SEMATTRS_HTTP_SCHEME]: url.protocol, - [SEMATTRS_NET_HOST_NAME]: hostname, - [SEMATTRS_HTTP_HOST]: host || undefined, - [SEMATTRS_HTTP_CLIENT_IP]: ips?.split(',')[0], - [SEMATTRS_HTTP_USER_AGENT]: userAgent || undefined, + [SEMATTRS_NET_HOST_NAME]: + url.hostname || + url.host || + request.headers.get('host') || + 'localhost', + [SEMATTRS_HTTP_HOST]: + url.host || request.headers.get('host') || undefined, + [SEMATTRS_HTTP_CLIENT_IP]: request.headers + .get('x-forwarded-for') + ?.split(',')[0], + [SEMATTRS_HTTP_USER_AGENT]: + request.headers.get('user-agent') || undefined, }, kind: SpanKind.SERVER, }, - otelContext, + input.ctx, ); + + return { + ctx: trace.setSpan(input.ctx, span), + }; +} + +export function endHttpSpan(ctx: Context, response: Response) { + const span = trace.getSpan(ctx); + if (span) { + span.setAttribute(SEMATTRS_HTTP_STATUS_CODE, response.status); + span.setStatus({ + code: response.ok ? SpanStatusCode.OK : SpanStatusCode.ERROR, + message: response.ok ? undefined : response.statusText, + }); + span.end(); + } +} + +export function startGraphQLSpan(input: { + ctx: Context; + tracer: Tracer; + params: GraphQLParams; +}): { + ctx: Context; + done: ( + result: MaybePromise>, + ) => void; +} { + const operationName = input.params.operationName ?? 'unknown'; + const span = input.tracer.startSpan( + `graphql.operation ${operationName}`, + { + attributes: { + [SEMATTRS_GRAPHQL_DOCUMENT]: input.params.query, + [SEMATTRS_GRAPHQL_OPERATION_NAME]: operationName, + }, + kind: SpanKind.INTERNAL, + }, + input.ctx, + ); + + return { + ctx: trace.setSpan(input.ctx, span), + done: (result$) => { + return mapMaybePromise( + result$, + () => { + //FIXME: handle async iterable results. + span.end(); + }, + (err) => { + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }); + span.recordException(err); + span.end(); + throw err; + }, + ); + }, + }; +} + +export function addExecutionArgsToGraphqlSpan( + ctx: Context, + args: ExecutionArgs, +) { + const span = trace.getSpan(ctx); + if (span) { + const operation = getOperationASTFromDocument( + args.document, + args.operationName || undefined, + ); + const operationName = operation.name?.value ?? 'Anonymous'; + const document = defaultPrintFn(args.document); + span.setAttribute(SEMATTRS_GRAPHQL_OPERATION_TYPE, operation.operation); + span.setAttribute(SEMATTRS_GRAPHQL_OPERATION_NAME, operationName); + span.setAttribute(SEMATTRS_GRAPHQL_DOCUMENT, document); + span.updateName(`graphql.operation ${operationName}`); + } } -export function completeHttpSpan(span: Span, response: Response) { - span.setAttribute(SEMATTRS_HTTP_STATUS_CODE, response.status); - span.setStatus({ - code: response.ok ? SpanStatusCode.OK : SpanStatusCode.ERROR, - message: response.ok ? undefined : response.statusText, - }); - span.end(); +export function createGraphqlContextBuildingSpan(input: { + ctx: Context; + tracer: Tracer; +}): { ctx: Context; done: () => void } { + const span = input.tracer.startSpan( + 'graphql.context', + { kind: SpanKind.INTERNAL }, + input.ctx, + ); + + return { + ctx: trace.setSpan(input.ctx, span), + done: () => span.end(), + }; } -export function createGraphQLParseSpan(input: { - otelContext: Context; +export function startGraphQLParseSpan(input: { + ctx: Context; tracer: Tracer; query?: string; operationName?: string; -}) { - const parseSpan = input.tracer.startSpan( +}): { ctx: Context; done: (result: unknown) => void } { + const span = input.tracer.startSpan( 'graphql.parse', { attributes: { @@ -87,33 +180,32 @@ export function createGraphQLParseSpan(input: { }, kind: SpanKind.INTERNAL, }, - input.otelContext, + input.ctx, ); return { - parseSpan, - done: (result: any | Error | null) => { + ctx: trace.setSpan(input.ctx, span), + done: (result) => { if (result instanceof Error) { - parseSpan.setAttribute(SEMATTRS_GRAPHQL_ERROR_COUNT, 1); - parseSpan.recordException(result); - parseSpan.setStatus({ + span.setAttribute(SEMATTRS_GRAPHQL_ERROR_COUNT, 1); + span.recordException(result); + span.setStatus({ code: SpanStatusCode.ERROR, message: result.message, }); } - - parseSpan.end(); + span.end(); }, }; } -export function createGraphQLValidateSpan(input: { - otelContext: Context; +export function startGraphQLValidateSpan(input: { + ctx: Context; tracer: Tracer; query?: string; operationName?: string; -}) { - const validateSpan = input.tracer.startSpan( +}): { ctx: Context; done: (result: any[] | readonly Error[]) => void } { + const span = input.tracer.startSpan( 'graphql.validate', { attributes: { @@ -122,89 +214,94 @@ export function createGraphQLValidateSpan(input: { }, kind: SpanKind.INTERNAL, }, - input.otelContext, + input.ctx, ); - return { - validateSpan, - done: (result: any[] | readonly Error[]) => { + ctx: trace.setSpan(input.ctx, span), + done: (result) => { if (result instanceof Error) { - validateSpan.setStatus({ + span.setStatus({ code: SpanStatusCode.ERROR, message: result.message, }); } else if (Array.isArray(result) && result.length > 0) { - validateSpan.setAttribute(SEMATTRS_GRAPHQL_ERROR_COUNT, result.length); - validateSpan.setStatus({ + span.setAttribute(SEMATTRS_GRAPHQL_ERROR_COUNT, result.length); + span.setStatus({ code: SpanStatusCode.ERROR, message: result.map((e) => e.message).join(', '), }); for (const error in result) { - validateSpan.recordException(error); + span.recordException(error); } } - - validateSpan.end(); + span.end(); }, }; } -export function createGraphQLExecuteSpan(input: { +export function startGraphQLExecuteSpan(input: { + ctx: Context; args: ExecutionArgs; - otelContext: Context; tracer: Tracer; -}) { +}): { + ctx: Context; + done: ( + result: ExecutionResult | AsyncIterableIterator, + ) => void; +} { const operation = getOperationASTFromDocument( input.args.document, input.args.operationName || undefined, ); - const executeSpan = input.tracer.startSpan( + const operationName = operation.name?.value ?? 'Anonymous'; + const document = defaultPrintFn(input.args.document); + const span = input.tracer.startSpan( 'graphql.execute', { attributes: { [SEMATTRS_GRAPHQL_OPERATION_TYPE]: operation.operation, - [SEMATTRS_GRAPHQL_OPERATION_NAME]: - input.args.operationName || undefined, - [SEMATTRS_GRAPHQL_DOCUMENT]: defaultPrintFn(input.args.document), + [SEMATTRS_GRAPHQL_OPERATION_NAME]: operationName, + [SEMATTRS_GRAPHQL_DOCUMENT]: document, }, kind: SpanKind.INTERNAL, }, - input.otelContext, + input.ctx, ); return { - executeSpan, - done: (result: ExecutionResult) => { - if (result.errors && result.errors.length > 0) { - executeSpan.setAttribute( - SEMATTRS_GRAPHQL_ERROR_COUNT, - result.errors.length, - ); - executeSpan.setStatus({ + ctx: trace.setSpan(input.ctx, span), + done: (result) => { + if ( + !isAsyncIterable(result) && // FIXME: Handle async iterable too + result.errors && + result.errors.length > 0 + ) { + span.setAttribute(SEMATTRS_GRAPHQL_ERROR_COUNT, result.errors.length); + span.setStatus({ code: SpanStatusCode.ERROR, message: result.errors.map((e) => e.message).join(', '), }); - for (const error in result.errors) { - executeSpan.recordException(error); + for (const error of result.errors) { + span.recordException(error); } } - - executeSpan.end(); + span.end(); }, }; } -export const subgraphExecReqSpanMap = new WeakMap(); - -export function createSubgraphExecuteFetchSpan(input: { - otelContext: Context; +export function startSubgraphExecuteFetchSpan(input: { + ctx: Context; tracer: Tracer; executionRequest: ExecutionRequest; subgraphName: string; -}) { - const subgraphExecuteSpan = input.tracer.startSpan( +}): { + ctx: Context; + done: (result: unknown) => void; +} { + const span = input.tracer.startSpan( `subgraph.execute (${input.subgraphName})`, { attributes: { @@ -220,69 +317,50 @@ export function createSubgraphExecuteFetchSpan(input: { }, kind: SpanKind.CLIENT, }, - input.otelContext, + input.ctx, ); - subgraphExecReqSpanMap.set(input.executionRequest, subgraphExecuteSpan); - return { - done() { - subgraphExecuteSpan.end(); + ctx: trace.setSpan(input.ctx, span), + done: () => { + return span.end(); }, }; } -export function createUpstreamHttpFetchSpan(input: { - otelContext: Context; +export function startUpstreamHttpFetchSpan(input: { + ctx: Context; tracer: Tracer; url: string; fetchOptions: RequestInit; executionRequest?: ExecutionRequest; -}) { +}): { ctx: Context; done: (response: Response) => void } { const urlObj = new URL(input.url); - - const attributes = { - [SEMATTRS_HTTP_METHOD]: input.fetchOptions.method, - [SEMATTRS_HTTP_URL]: input.url, - [SEMATTRS_NET_HOST_NAME]: urlObj.hostname, - [SEMATTRS_HTTP_HOST]: urlObj.host, - [SEMATTRS_HTTP_ROUTE]: urlObj.pathname, - [SEMATTRS_HTTP_SCHEME]: urlObj.protocol, - }; - - let fetchSpan: Span | undefined; - let isOrigSpan: boolean; - - if (input.executionRequest) { - fetchSpan = subgraphExecReqSpanMap.get(input.executionRequest); - if (fetchSpan) { - isOrigSpan = false; - fetchSpan.setAttributes(attributes); - } - } - - if (!fetchSpan) { - fetchSpan = input.tracer.startSpan( - 'http.fetch', - { - attributes, - kind: SpanKind.CLIENT, + const span = input.tracer.startSpan( + 'http.fetch', + { + attributes: { + [SEMATTRS_HTTP_METHOD]: input.fetchOptions.method, + [SEMATTRS_HTTP_URL]: input.url, + [SEMATTRS_NET_HOST_NAME]: urlObj.hostname, + [SEMATTRS_HTTP_HOST]: urlObj.host, + [SEMATTRS_HTTP_ROUTE]: urlObj.pathname, + [SEMATTRS_HTTP_SCHEME]: urlObj.protocol, }, - input.otelContext, - ); - isOrigSpan = true; - } + kind: SpanKind.CLIENT, + }, + input.ctx, + ); return { - done: (response: Response) => { - fetchSpan.setAttribute(SEMATTRS_HTTP_STATUS_CODE, response.status); - fetchSpan.setStatus({ + ctx: trace.setSpan(input.ctx, span), + done: (response) => { + span.setAttribute(SEMATTRS_HTTP_STATUS_CODE, response.status); + span.setStatus({ code: response.ok ? SpanStatusCode.OK : SpanStatusCode.ERROR, message: response.ok ? undefined : response.statusText, }); - if (isOrigSpan) { - fetchSpan.end(); - } + span.end(); }, }; } diff --git a/packages/plugins/opentelemetry/src/utils.ts b/packages/plugins/opentelemetry/src/utils.ts new file mode 100644 index 000000000..eff7a3581 --- /dev/null +++ b/packages/plugins/opentelemetry/src/utils.ts @@ -0,0 +1,48 @@ +import { isPromise } from 'util/types'; +import { + isPromise as isPromiseLike, + mapMaybePromise as mapMaybePromiseLike, + type MaybePromise, +} from '@graphql-tools/utils'; + +function mapMaybePromise( + value: Promise | T, + mapper: (value: T) => Promise | R, + errorMapper?: (err: unknown) => Promise | R, +): Promise | R { + const res$ = mapMaybePromiseLike(value, mapper, errorMapper); + if (isPromiseLike(res$)) { + return toPromise(res$); + } + return res$; +} +export { mapMaybePromise, mapMaybePromiseLike }; + +export function toPromise(mp: MaybePromise): Promise { + if (isPromise(mp)) { + return mp as Promise; + } + if (isPromiseLike(mp)) { + return { + then: (onfullfilled, onrejected) => + toPromise(mp.then(onfullfilled, onrejected)), + catch: (onrejected) => toPromise(mp.then(null, onrejected)), + finally: (onfinally) => { + return toPromise( + mp.then( + (res) => { + onfinally?.(); + return res; + }, + (err) => { + onfinally?.(); + throw err; + }, + ), + ); + }, + [Symbol.toStringTag]: 'Promise', + }; + } + return Promise.resolve(mp); +} diff --git a/yarn.lock b/yarn.lock index db3a936e7..e7c7b81a5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2738,13 +2738,13 @@ __metadata: languageName: node linkType: hard -"@envelop/core@npm:^5.0.0, @envelop/core@npm:^5.0.2, @envelop/core@npm:^5.0.3": - version: 5.0.3 - resolution: "@envelop/core@npm:5.0.3" +"@envelop/core@npm:5.1.0-alpha-20250206123608-cd323a08c43c066eb34cba698d61b4f059ab5ee5": + version: 5.1.0-alpha-20250206123608-cd323a08c43c066eb34cba698d61b4f059ab5ee5 + resolution: "@envelop/core@npm:5.1.0-alpha-20250206123608-cd323a08c43c066eb34cba698d61b4f059ab5ee5" dependencies: - "@envelop/types": "npm:5.0.0" + "@envelop/types": "npm:5.1.0-alpha-20250206123608-cd323a08c43c066eb34cba698d61b4f059ab5ee5" tslib: "npm:^2.5.0" - checksum: 10c0/86f181137a4062554357151c717f068c89897364de0fd989b9abb408b03b4e8fc5946f8cdd76f2ff3ba5ab2e74140acf82210b18c32bb86875f2f18079bc19e2 + checksum: 10c0/0063d642ee434276434df08d5712e2d142139d62518b4dc8e638970da20e836db472063071f07f2462e2b7410e0d236a6d46e9147d06b0c6c7b3c03ab7ef92ef languageName: node linkType: hard @@ -2874,12 +2874,12 @@ __metadata: languageName: node linkType: hard -"@envelop/types@npm:5.0.0": - version: 5.0.0 - resolution: "@envelop/types@npm:5.0.0" +"@envelop/types@npm:5.1.0-alpha-20250206123608-cd323a08c43c066eb34cba698d61b4f059ab5ee5": + version: 5.1.0-alpha-20250206123608-cd323a08c43c066eb34cba698d61b4f059ab5ee5 + resolution: "@envelop/types@npm:5.1.0-alpha-20250206123608-cd323a08c43c066eb34cba698d61b4f059ab5ee5" dependencies: tslib: "npm:^2.5.0" - checksum: 10c0/0cbaa68218cb6121b58c6d354b0a17913ded042673df7bfcf385cac6c3b42713b82719875f553b31e8f059727ff5478ed11b33b4febf8deeaf902f1a92b212a8 + checksum: 10c0/a34d3e93e1f647c48db9cee9709739e50478e6c0c80f269aeef9bf0b2d802558f78eb6450fa62ff81b067bbb2f0429ad332ed0ba03da3b924a35c74f90a8dc1e languageName: node linkType: hard @@ -3671,6 +3671,7 @@ __metadata: "@graphql-mesh/utils": "npm:^0.103.6" "@graphql-tools/utils": "npm:^10.7.0" "@opentelemetry/api": "npm:^1.9.0" + "@opentelemetry/context-async-hooks": "npm:^1.30.1" "@opentelemetry/exporter-trace-otlp-grpc": "npm:^0.57.0" "@opentelemetry/exporter-trace-otlp-http": "npm:^0.57.0" "@opentelemetry/exporter-zipkin": "npm:^1.29.0" @@ -3680,6 +3681,7 @@ __metadata: "@opentelemetry/sdk-trace-web": "npm:^1.29.0" "@opentelemetry/semantic-conventions": "npm:^1.28.0" "@whatwg-node/disposablestack": "npm:^0.0.5" + "@whatwg-node/server": "npm:^0.9.65" graphql: "npm:^16.9.0" graphql-yoga: "npm:^5.10.11" pkgroll: "npm:2.6.1" @@ -5425,6 +5427,15 @@ __metadata: languageName: node linkType: hard +"@opentelemetry/context-async-hooks@npm:^1.30.1": + version: 1.30.1 + resolution: "@opentelemetry/context-async-hooks@npm:1.30.1" + peerDependencies: + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: 10c0/3e8114d360060a5225226d2fcd8df08cd542246003790a7f011c0774bc60b8a931f46f4c6673f3977a7d9bba717de6ee028cae51b752c2567053d7f46ed3eba3 + languageName: node + linkType: hard + "@opentelemetry/core@npm:1.26.0": version: 1.26.0 resolution: "@opentelemetry/core@npm:1.26.0" @@ -7247,7 +7258,7 @@ __metadata: languageName: node linkType: hard -"@whatwg-node/server@npm:^0.9.55, @whatwg-node/server@npm:^0.9.60, @whatwg-node/server@npm:^0.9.64": +"@whatwg-node/server@npm:^0.9.55, @whatwg-node/server@npm:^0.9.60, @whatwg-node/server@npm:^0.9.64, @whatwg-node/server@npm:^0.9.65": version: 0.9.65 resolution: "@whatwg-node/server@npm:0.9.65" dependencies: