diff --git a/backend/pkg/messages/messages.go b/backend/pkg/messages/messages.go index 9f82d47cb..5b0c4acee 100644 --- a/backend/pkg/messages/messages.go +++ b/backend/pkg/messages/messages.go @@ -44,7 +44,7 @@ const ( MsgVuex = 45 MsgMobX = 46 MsgNgRx = 47 - MsgGraphQL = 48 + MsgGraphQLDeprecated = 48 MsgPerformanceTrack = 49 MsgStringDict = 50 MsgSetNodeAttributeDict = 51 @@ -90,6 +90,7 @@ const ( MsgTagTrigger = 120 MsgRedux = 121 MsgSetPageLocation = 122 + MsgGraphQL = 123 MsgIssueEvent = 125 MsgSessionEnd = 126 MsgSessionSearch = 127 @@ -1203,30 +1204,32 @@ func (msg *NgRx) TypeID() int { return 47 } -type GraphQL struct { +type GraphQLDeprecated struct { message OperationKind string OperationName string Variables string Response string + Duration int64 } -func (msg *GraphQL) Encode() []byte { - buf := make([]byte, 41+len(msg.OperationKind)+len(msg.OperationName)+len(msg.Variables)+len(msg.Response)) +func (msg *GraphQLDeprecated) Encode() []byte { + buf := make([]byte, 51+len(msg.OperationKind)+len(msg.OperationName)+len(msg.Variables)+len(msg.Response)) buf[0] = 48 p := 1 p = WriteString(msg.OperationKind, buf, p) p = WriteString(msg.OperationName, buf, p) p = WriteString(msg.Variables, buf, p) p = WriteString(msg.Response, buf, p) + p = WriteInt(msg.Duration, buf, p) return buf[:p] } -func (msg *GraphQL) Decode() Message { +func (msg *GraphQLDeprecated) Decode() Message { return msg } -func (msg *GraphQL) TypeID() int { +func (msg *GraphQLDeprecated) TypeID() int { return 48 } @@ -2411,6 +2414,35 @@ func (msg *SetPageLocation) TypeID() int { return 122 } +type GraphQL struct { + message + OperationKind string + OperationName string + Variables string + Response string + Duration uint64 +} + +func (msg *GraphQL) Encode() []byte { + buf := make([]byte, 51+len(msg.OperationKind)+len(msg.OperationName)+len(msg.Variables)+len(msg.Response)) + buf[0] = 123 + p := 1 + p = WriteString(msg.OperationKind, buf, p) + p = WriteString(msg.OperationName, buf, p) + p = WriteString(msg.Variables, buf, p) + p = WriteString(msg.Response, buf, p) + p = WriteUint(msg.Duration, buf, p) + return buf[:p] +} + +func (msg *GraphQL) Decode() Message { + return msg +} + +func (msg *GraphQL) TypeID() int { + return 123 +} + type IssueEvent struct { message MessageID uint64 diff --git a/backend/pkg/messages/read-message.go b/backend/pkg/messages/read-message.go index 4ef9ba53f..4ddde9c3b 100644 --- a/backend/pkg/messages/read-message.go +++ b/backend/pkg/messages/read-message.go @@ -693,9 +693,9 @@ func DecodeNgRx(reader BytesReader) (Message, error) { return msg, err } -func DecodeGraphQL(reader BytesReader) (Message, error) { +func DecodeGraphQLDeprecated(reader BytesReader) (Message, error) { var err error = nil - msg := &GraphQL{} + msg := &GraphQLDeprecated{} if msg.OperationKind, err = reader.ReadString(); err != nil { return nil, err } @@ -708,6 +708,9 @@ func DecodeGraphQL(reader BytesReader) (Message, error) { if msg.Response, err = reader.ReadString(); err != nil { return nil, err } + if msg.Duration, err = reader.ReadInt(); err != nil { + return nil, err + } return msg, err } @@ -1470,6 +1473,27 @@ func DecodeSetPageLocation(reader BytesReader) (Message, error) { return msg, err } +func DecodeGraphQL(reader BytesReader) (Message, error) { + var err error = nil + msg := &GraphQL{} + if msg.OperationKind, err = reader.ReadString(); err != nil { + return nil, err + } + if msg.OperationName, err = reader.ReadString(); err != nil { + return nil, err + } + if msg.Variables, err = reader.ReadString(); err != nil { + return nil, err + } + if msg.Response, err = reader.ReadString(); err != nil { + return nil, err + } + if msg.Duration, err = reader.ReadUint(); err != nil { + return nil, err + } + return msg, err +} + func DecodeIssueEvent(reader BytesReader) (Message, error) { var err error = nil msg := &IssueEvent{} @@ -2019,7 +2043,7 @@ func ReadMessage(t uint64, reader BytesReader) (Message, error) { case 47: return DecodeNgRx(reader) case 48: - return DecodeGraphQL(reader) + return DecodeGraphQLDeprecated(reader) case 49: return DecodePerformanceTrack(reader) case 50: @@ -2110,6 +2134,8 @@ func ReadMessage(t uint64, reader BytesReader) (Message, error) { return DecodeRedux(reader) case 122: return DecodeSetPageLocation(reader) + case 123: + return DecodeGraphQL(reader) case 125: return DecodeIssueEvent(reader) case 126: diff --git a/ee/connectors/msgcodec/messages.py b/ee/connectors/msgcodec/messages.py index 5839934b0..f14b05f92 100644 --- a/ee/connectors/msgcodec/messages.py +++ b/ee/connectors/msgcodec/messages.py @@ -404,14 +404,15 @@ class NgRx(Message): self.duration = duration -class GraphQL(Message): +class GraphQLDeprecated(Message): __id__ = 48 - def __init__(self, operation_kind, operation_name, variables, response): + def __init__(self, operation_kind, operation_name, variables, response, duration): self.operation_kind = operation_kind self.operation_name = operation_name self.variables = variables self.response = response + self.duration = duration class PerformanceTrack(Message): @@ -847,6 +848,17 @@ class SetPageLocation(Message): self.document_title = document_title +class GraphQL(Message): + __id__ = 123 + + def __init__(self, operation_kind, operation_name, variables, response, duration): + self.operation_kind = operation_kind + self.operation_name = operation_name + self.variables = variables + self.response = response + self.duration = duration + + class IssueEvent(Message): __id__ = 125 diff --git a/ee/connectors/msgcodec/messages.pyx b/ee/connectors/msgcodec/messages.pyx index bbbb4257f..d495bfaf4 100644 --- a/ee/connectors/msgcodec/messages.pyx +++ b/ee/connectors/msgcodec/messages.pyx @@ -596,19 +596,21 @@ cdef class NgRx(PyMessage): self.duration = duration -cdef class GraphQL(PyMessage): +cdef class GraphQLDeprecated(PyMessage): cdef public int __id__ cdef public str operation_kind cdef public str operation_name cdef public str variables cdef public str response + cdef public long duration - def __init__(self, str operation_kind, str operation_name, str variables, str response): + def __init__(self, str operation_kind, str operation_name, str variables, str response, long duration): self.__id__ = 48 self.operation_kind = operation_kind self.operation_name = operation_name self.variables = variables self.response = response + self.duration = duration cdef class PerformanceTrack(PyMessage): @@ -1252,6 +1254,23 @@ cdef class SetPageLocation(PyMessage): self.document_title = document_title +cdef class GraphQL(PyMessage): + cdef public int __id__ + cdef public str operation_kind + cdef public str operation_name + cdef public str variables + cdef public str response + cdef public unsigned long duration + + def __init__(self, str operation_kind, str operation_name, str variables, str response, unsigned long duration): + self.__id__ = 123 + self.operation_kind = operation_kind + self.operation_name = operation_name + self.variables = variables + self.response = response + self.duration = duration + + cdef class IssueEvent(PyMessage): cdef public int __id__ cdef public unsigned long message_id diff --git a/ee/connectors/msgcodec/msgcodec.py b/ee/connectors/msgcodec/msgcodec.py index c4b88569b..6a4a73bdc 100644 --- a/ee/connectors/msgcodec/msgcodec.py +++ b/ee/connectors/msgcodec/msgcodec.py @@ -408,11 +408,12 @@ class MessageCodec(Codec): ) if message_id == 48: - return GraphQL( + return GraphQLDeprecated( operation_kind=self.read_string(reader), operation_name=self.read_string(reader), variables=self.read_string(reader), - response=self.read_string(reader) + response=self.read_string(reader), + duration=self.read_int(reader) ) if message_id == 49: @@ -758,6 +759,15 @@ class MessageCodec(Codec): document_title=self.read_string(reader) ) + if message_id == 123: + return GraphQL( + operation_kind=self.read_string(reader), + operation_name=self.read_string(reader), + variables=self.read_string(reader), + response=self.read_string(reader), + duration=self.read_uint(reader) + ) + if message_id == 125: return IssueEvent( message_id=self.read_uint(reader), diff --git a/ee/connectors/msgcodec/msgcodec.pyx b/ee/connectors/msgcodec/msgcodec.pyx index 791f7fae9..b7f9e105e 100644 --- a/ee/connectors/msgcodec/msgcodec.pyx +++ b/ee/connectors/msgcodec/msgcodec.pyx @@ -506,11 +506,12 @@ cdef class MessageCodec: ) if message_id == 48: - return GraphQL( + return GraphQLDeprecated( operation_kind=self.read_string(reader), operation_name=self.read_string(reader), variables=self.read_string(reader), - response=self.read_string(reader) + response=self.read_string(reader), + duration=self.read_int(reader) ) if message_id == 49: @@ -856,6 +857,15 @@ cdef class MessageCodec: document_title=self.read_string(reader) ) + if message_id == 123: + return GraphQL( + operation_kind=self.read_string(reader), + operation_name=self.read_string(reader), + variables=self.read_string(reader), + response=self.read_string(reader), + duration=self.read_uint(reader) + ) + if message_id == 125: return IssueEvent( message_id=self.read_uint(reader), diff --git a/frontend/app/player/web/TabManager.ts b/frontend/app/player/web/TabManager.ts index 7c0b5c4d1..a61a1c656 100644 --- a/frontend/app/player/web/TabManager.ts +++ b/frontend/app/player/web/TabManager.ts @@ -263,6 +263,7 @@ export default class TabSessionManager { case MType.MobX: this.lists.lists.mobx.append(msg); break; + case MType.GraphQlDeprecated: case MType.GraphQl: this.lists.lists.graphql.append(msg); break; diff --git a/frontend/app/player/web/messages/RawMessageReader.gen.ts b/frontend/app/player/web/messages/RawMessageReader.gen.ts index 451859a7e..10b178914 100644 --- a/frontend/app/player/web/messages/RawMessageReader.gen.ts +++ b/frontend/app/player/web/messages/RawMessageReader.gen.ts @@ -348,12 +348,14 @@ export default class RawMessageReader extends PrimitiveReader { const operationName = this.readString(); if (operationName === null) { return resetPointer() } const variables = this.readString(); if (variables === null) { return resetPointer() } const response = this.readString(); if (response === null) { return resetPointer() } + const duration = this.readInt(); if (duration === null) { return resetPointer() } return { - tp: MType.GraphQl, + tp: MType.GraphQlDeprecated, operationKind, operationName, variables, response, + duration, }; } @@ -795,6 +797,22 @@ export default class RawMessageReader extends PrimitiveReader { }; } + case 123: { + const operationKind = this.readString(); if (operationKind === null) { return resetPointer() } + const operationName = this.readString(); if (operationName === null) { return resetPointer() } + const variables = this.readString(); if (variables === null) { return resetPointer() } + const response = this.readString(); if (response === null) { return resetPointer() } + const duration = this.readUint(); if (duration === null) { return resetPointer() } + return { + tp: MType.GraphQl, + operationKind, + operationName, + variables, + response, + duration, + }; + } + case 93: { const timestamp = this.readUint(); if (timestamp === null) { return resetPointer() } const length = this.readUint(); if (length === null) { return resetPointer() } diff --git a/frontend/app/player/web/messages/message.gen.ts b/frontend/app/player/web/messages/message.gen.ts index 3f6dc2ea1..d570101a0 100644 --- a/frontend/app/player/web/messages/message.gen.ts +++ b/frontend/app/player/web/messages/message.gen.ts @@ -32,7 +32,7 @@ import type { RawVuex, RawMobX, RawNgRx, - RawGraphQl, + RawGraphQlDeprecated, RawPerformanceTrack, RawStringDict, RawSetNodeAttributeDict, @@ -67,6 +67,7 @@ import type { RawTagTrigger, RawRedux, RawSetPageLocation, + RawGraphQl, RawMobileEvent, RawMobileScreenChanges, RawMobileClickEvent, @@ -138,7 +139,7 @@ export type MobX = RawMobX & Timed export type NgRx = RawNgRx & Timed -export type GraphQl = RawGraphQl & Timed +export type GraphQlDeprecated = RawGraphQlDeprecated & Timed export type PerformanceTrack = RawPerformanceTrack & Timed @@ -208,6 +209,8 @@ export type Redux = RawRedux & Timed export type SetPageLocation = RawSetPageLocation & Timed +export type GraphQl = RawGraphQl & Timed + export type MobileEvent = RawMobileEvent & Timed export type MobileScreenChanges = RawMobileScreenChanges & Timed diff --git a/frontend/app/player/web/messages/raw.gen.ts b/frontend/app/player/web/messages/raw.gen.ts index ddf578aca..a62a8d3d7 100644 --- a/frontend/app/player/web/messages/raw.gen.ts +++ b/frontend/app/player/web/messages/raw.gen.ts @@ -30,7 +30,7 @@ export const enum MType { Vuex = 45, MobX = 46, NgRx = 47, - GraphQl = 48, + GraphQlDeprecated = 48, PerformanceTrack = 49, StringDict = 50, SetNodeAttributeDict = 51, @@ -65,6 +65,7 @@ export const enum MType { TagTrigger = 120, Redux = 121, SetPageLocation = 122, + GraphQl = 123, MobileEvent = 93, MobileScreenChanges = 96, MobileClickEvent = 100, @@ -268,12 +269,13 @@ export interface RawNgRx { duration: number, } -export interface RawGraphQl { - tp: MType.GraphQl, +export interface RawGraphQlDeprecated { + tp: MType.GraphQlDeprecated, operationKind: string, operationName: string, variables: string, response: string, + duration: number, } export interface RawPerformanceTrack { @@ -529,6 +531,15 @@ export interface RawSetPageLocation { documentTitle: string, } +export interface RawGraphQl { + tp: MType.GraphQl, + operationKind: string, + operationName: string, + variables: string, + response: string, + duration: number, +} + export interface RawMobileEvent { tp: MType.MobileEvent, timestamp: number, @@ -621,4 +632,4 @@ export interface RawMobileIssueEvent { } -export type RawMessage = RawTimestamp | RawSetPageLocationDeprecated | RawSetViewportSize | RawSetViewportScroll | RawCreateDocument | RawCreateElementNode | RawCreateTextNode | RawMoveNode | RawRemoveNode | RawSetNodeAttribute | RawRemoveNodeAttribute | RawSetNodeData | RawSetCssData | RawSetNodeScroll | RawSetInputValue | RawSetInputChecked | RawMouseMove | RawNetworkRequestDeprecated | RawConsoleLog | RawCssInsertRule | RawCssDeleteRule | RawFetch | RawProfiler | RawOTable | RawReduxDeprecated | RawVuex | RawMobX | RawNgRx | RawGraphQl | RawPerformanceTrack | RawStringDict | RawSetNodeAttributeDict | RawResourceTimingDeprecated | RawConnectionInformation | RawSetPageVisibility | RawLoadFontFace | RawSetNodeFocus | RawLongTask | RawSetNodeAttributeURLBased | RawSetCssDataURLBased | RawCssInsertRuleURLBased | RawMouseClick | RawMouseClickDeprecated | RawCreateIFrameDocument | RawAdoptedSsReplaceURLBased | RawAdoptedSsReplace | RawAdoptedSsInsertRuleURLBased | RawAdoptedSsInsertRule | RawAdoptedSsDeleteRule | RawAdoptedSsAddOwner | RawAdoptedSsRemoveOwner | RawZustand | RawNetworkRequest | RawWsChannel | RawSelectionChange | RawMouseThrashing | RawResourceTiming | RawTabChange | RawTabData | RawCanvasNode | RawTagTrigger | RawRedux | RawSetPageLocation | RawMobileEvent | RawMobileScreenChanges | RawMobileClickEvent | RawMobileInputEvent | RawMobilePerformanceEvent | RawMobileLog | RawMobileInternalError | RawMobileNetworkCall | RawMobileSwipeEvent | RawMobileIssueEvent; +export type RawMessage = RawTimestamp | RawSetPageLocationDeprecated | RawSetViewportSize | RawSetViewportScroll | RawCreateDocument | RawCreateElementNode | RawCreateTextNode | RawMoveNode | RawRemoveNode | RawSetNodeAttribute | RawRemoveNodeAttribute | RawSetNodeData | RawSetCssData | RawSetNodeScroll | RawSetInputValue | RawSetInputChecked | RawMouseMove | RawNetworkRequestDeprecated | RawConsoleLog | RawCssInsertRule | RawCssDeleteRule | RawFetch | RawProfiler | RawOTable | RawReduxDeprecated | RawVuex | RawMobX | RawNgRx | RawGraphQlDeprecated | RawPerformanceTrack | RawStringDict | RawSetNodeAttributeDict | RawResourceTimingDeprecated | RawConnectionInformation | RawSetPageVisibility | RawLoadFontFace | RawSetNodeFocus | RawLongTask | RawSetNodeAttributeURLBased | RawSetCssDataURLBased | RawCssInsertRuleURLBased | RawMouseClick | RawMouseClickDeprecated | RawCreateIFrameDocument | RawAdoptedSsReplaceURLBased | RawAdoptedSsReplace | RawAdoptedSsInsertRuleURLBased | RawAdoptedSsInsertRule | RawAdoptedSsDeleteRule | RawAdoptedSsAddOwner | RawAdoptedSsRemoveOwner | RawZustand | RawNetworkRequest | RawWsChannel | RawSelectionChange | RawMouseThrashing | RawResourceTiming | RawTabChange | RawTabData | RawCanvasNode | RawTagTrigger | RawRedux | RawSetPageLocation | RawGraphQl | RawMobileEvent | RawMobileScreenChanges | RawMobileClickEvent | RawMobileInputEvent | RawMobilePerformanceEvent | RawMobileLog | RawMobileInternalError | RawMobileNetworkCall | RawMobileSwipeEvent | RawMobileIssueEvent; diff --git a/frontend/app/player/web/messages/tracker-legacy.gen.ts b/frontend/app/player/web/messages/tracker-legacy.gen.ts index e942a32e2..3404bad15 100644 --- a/frontend/app/player/web/messages/tracker-legacy.gen.ts +++ b/frontend/app/player/web/messages/tracker-legacy.gen.ts @@ -31,7 +31,7 @@ export const TP_MAP = { 45: MType.Vuex, 46: MType.MobX, 47: MType.NgRx, - 48: MType.GraphQl, + 48: MType.GraphQlDeprecated, 49: MType.PerformanceTrack, 50: MType.StringDict, 51: MType.SetNodeAttributeDict, @@ -66,6 +66,7 @@ export const TP_MAP = { 120: MType.TagTrigger, 121: MType.Redux, 122: MType.SetPageLocation, + 123: MType.GraphQl, 93: MType.MobileEvent, 96: MType.MobileScreenChanges, 100: MType.MobileClickEvent, diff --git a/frontend/app/player/web/messages/tracker.gen.ts b/frontend/app/player/web/messages/tracker.gen.ts index 18c9a9e84..cdf0be6c9 100644 --- a/frontend/app/player/web/messages/tracker.gen.ts +++ b/frontend/app/player/web/messages/tracker.gen.ts @@ -242,12 +242,13 @@ type TrNgRx = [ duration: number, ] -type TrGraphQL = [ +type TrGraphQLDeprecated = [ type: 48, operationKind: string, operationName: string, variables: string, response: string, + duration: number, ] type TrPerformanceTrack = [ @@ -540,8 +541,17 @@ type TrSetPageLocation = [ documentTitle: string, ] +type TrGraphQL = [ + type: 123, + operationKind: string, + operationName: string, + variables: string, + response: string, + duration: number, +] -export type TrackerMessage = TrTimestamp | TrSetPageLocationDeprecated | TrSetViewportSize | TrSetViewportScroll | TrCreateDocument | TrCreateElementNode | TrCreateTextNode | TrMoveNode | TrRemoveNode | TrSetNodeAttribute | TrRemoveNodeAttribute | TrSetNodeData | TrSetNodeScroll | TrSetInputTarget | TrSetInputValue | TrSetInputChecked | TrMouseMove | TrNetworkRequestDeprecated | TrConsoleLog | TrPageLoadTiming | TrPageRenderTiming | TrCustomEvent | TrUserID | TrUserAnonymousID | TrMetadata | TrCSSInsertRule | TrCSSDeleteRule | TrFetch | TrProfiler | TrOTable | TrStateAction | TrReduxDeprecated | TrVuex | TrMobX | TrNgRx | TrGraphQL | TrPerformanceTrack | TrStringDict | TrSetNodeAttributeDict | TrResourceTimingDeprecated | TrConnectionInformation | TrSetPageVisibility | TrLoadFontFace | TrSetNodeFocus | TrLongTask | TrSetNodeAttributeURLBased | TrSetCSSDataURLBased | TrTechnicalInfo | TrCustomIssue | TrCSSInsertRuleURLBased | TrMouseClick | TrMouseClickDeprecated | TrCreateIFrameDocument | TrAdoptedSSReplaceURLBased | TrAdoptedSSInsertRuleURLBased | TrAdoptedSSDeleteRule | TrAdoptedSSAddOwner | TrAdoptedSSRemoveOwner | TrJSException | TrZustand | TrBatchMetadata | TrPartitionedMessage | TrNetworkRequest | TrWSChannel | TrInputChange | TrSelectionChange | TrMouseThrashing | TrUnbindNodes | TrResourceTiming | TrTabChange | TrTabData | TrCanvasNode | TrTagTrigger | TrRedux | TrSetPageLocation + +export type TrackerMessage = TrTimestamp | TrSetPageLocationDeprecated | TrSetViewportSize | TrSetViewportScroll | TrCreateDocument | TrCreateElementNode | TrCreateTextNode | TrMoveNode | TrRemoveNode | TrSetNodeAttribute | TrRemoveNodeAttribute | TrSetNodeData | TrSetNodeScroll | TrSetInputTarget | TrSetInputValue | TrSetInputChecked | TrMouseMove | TrNetworkRequestDeprecated | TrConsoleLog | TrPageLoadTiming | TrPageRenderTiming | TrCustomEvent | TrUserID | TrUserAnonymousID | TrMetadata | TrCSSInsertRule | TrCSSDeleteRule | TrFetch | TrProfiler | TrOTable | TrStateAction | TrReduxDeprecated | TrVuex | TrMobX | TrNgRx | TrGraphQLDeprecated | TrPerformanceTrack | TrStringDict | TrSetNodeAttributeDict | TrResourceTimingDeprecated | TrConnectionInformation | TrSetPageVisibility | TrLoadFontFace | TrSetNodeFocus | TrLongTask | TrSetNodeAttributeURLBased | TrSetCSSDataURLBased | TrTechnicalInfo | TrCustomIssue | TrCSSInsertRuleURLBased | TrMouseClick | TrMouseClickDeprecated | TrCreateIFrameDocument | TrAdoptedSSReplaceURLBased | TrAdoptedSSInsertRuleURLBased | TrAdoptedSSDeleteRule | TrAdoptedSSAddOwner | TrAdoptedSSRemoveOwner | TrJSException | TrZustand | TrBatchMetadata | TrPartitionedMessage | TrNetworkRequest | TrWSChannel | TrInputChange | TrSelectionChange | TrMouseThrashing | TrUnbindNodes | TrResourceTiming | TrTabChange | TrTabData | TrCanvasNode | TrTagTrigger | TrRedux | TrSetPageLocation | TrGraphQL export default function translate(tMsg: TrackerMessage): RawMessage | null { switch(tMsg[0]) { @@ -786,11 +796,12 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null { case 48: { return { - tp: MType.GraphQl, + tp: MType.GraphQlDeprecated, operationKind: tMsg[1], operationName: tMsg[2], variables: tMsg[3], response: tMsg[4], + duration: tMsg[5], } } @@ -1098,6 +1109,17 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null { } } + case 123: { + return { + tp: MType.GraphQl, + operationKind: tMsg[1], + operationName: tMsg[2], + variables: tMsg[3], + response: tMsg[4], + duration: tMsg[5], + } + } + default: return null } diff --git a/mobs/messages.rb b/mobs/messages.rb index 7c768e61a..f5dfaed52 100644 --- a/mobs/messages.rb +++ b/mobs/messages.rb @@ -244,11 +244,12 @@ message 47, 'NgRx', :replayer => :devtools do string 'State' uint 'Duration' end -message 48, 'GraphQL', :replayer => :devtools do +message 48, 'GraphQLDeprecated', :replayer => :devtools do string 'OperationKind' string 'OperationName' string 'Variables' string 'Response' + int 'Duration' end message 49, 'PerformanceTrack' do #, :replayer => :devtools --> requires player performance refactoring (now is tied with nodes counter) int 'Frames' @@ -547,6 +548,14 @@ message 122, 'SetPageLocation' do string 'DocumentTitle' end +message 123, 'GraphQL', :replayer => :devtools do + string 'OperationKind' + string 'OperationName' + string 'Variables' + string 'Response' + uint 'Duration' +end + ## Backend-only message 125, 'IssueEvent', :replayer => false, :tracker => false do uint 'MessageID' diff --git a/tracker/tracker-graphql/README.md b/tracker/tracker-graphql/README.md index 4fe38734f..4cc3a4931 100644 --- a/tracker/tracker-graphql/README.md +++ b/tracker/tracker-graphql/README.md @@ -12,8 +12,9 @@ npm i @openreplay/tracker-graphql Initialize the `@openreplay/tracker` package as usual and load the plugin into it. The `plugin` call will return the function, which receives four variables -`operationKind`, `operationName`, `variables`, `result` -and returns `result` without changes. +`operationKind`, `operationName`, `variables`, `result` and `duration` (default 0) + +returns `result` without changes. ```js import Tracker from '@openreplay/tracker'; @@ -28,21 +29,45 @@ export const recordGraphQL = tracker.plugin(trackerGraphQL()); ### Relay -For [Relay](https://relay.dev/) you should manually put `recordGraphQL` call +If you're using [Relay network tools](https://github.com/relay-tools/react-relay-network-modern), +you can simply [create a middleware](https://github.com/relay-tools/react-relay-network-modern/tree/master?tab=readme-ov-file#example-of-injecting-networklayer-with-middlewares-on-the-client-side) + +```js +import { createRelayMiddleware } from '@openreplay/tracker-graphql' + +const trackerMiddleware = createRelayMiddleware(tracker) + +const network = new RelayNetworkLayer([ + // your middleware + // , + trackerMiddleware +]) +``` + +Or you can manually put `recordGraphQL` call to the `NetworkLayer` implementation. If you are standard `Network.create` way to implement it, then you should do something like below ```js -import { recordGraphQL } from 'tracker'; // see above for recordGraphQL definition +import { createGraphqlMiddleware } from '@openreplay/tracker-graphql'; // see above for recordGraphQL definition import { Environment } from 'relay-runtime'; +const handler = createGraphqlMiddleware(tracker) + function fetchQuery(operation, variables, cacheConfig, uploadables) { return fetch('www.myapi.com/resource', { // ... }) .then(response => response.json()) .then(result => - recordGraphQL(operation.operationKind, operation.name, variables, result), + handler( + // op kind, name, variables, response, duration (default 0) + operation.operationKind, + operation.name, + variables, + result, + duration, + ), ); } @@ -53,16 +78,27 @@ See [Relay Network Layer](https://relay.dev/docs/en/network-layer) for details. ### Apollo -For [Apollo](https://www.apollographql.com/) you should create a new `ApolloLink` with -`recordGraphQL` call and put it to your chain like below +For [Apollo](https://www.apollographql.com/) you should create a new `ApolloLink` ```js -import { recordGraphQL } from 'tracker'; // see above for recordGraphQL definition +import { createTrackerLink } from '@openreplay/tracker-graphql' + +const trackerLink = createTrackerLink(tracker); +const yourLink = new ApolloLink(trackerLink) +``` + +Alternatively you can use generic graphql handler: + +```js +import { createGraphqlMiddleware } from '@openreplay/tracker-graphql'; // see above for recordGraphQL definition import { ApolloLink } from 'apollo-link'; +const handler = createGraphqlMiddleware(tracker) + const trackerApolloLink = new ApolloLink((operation, forward) => { return forward(operation).map(result => - recordGraphQL( + handler( + // op kind, name, variables, response, duration (default 0) operation.query.definitions[0].operation, operation.operationName, operation.variables, diff --git a/tracker/tracker-graphql/bun.lockb b/tracker/tracker-graphql/bun.lockb new file mode 100755 index 000000000..9ba1a40db Binary files /dev/null and b/tracker/tracker-graphql/bun.lockb differ diff --git a/tracker/tracker-graphql/package.json b/tracker/tracker-graphql/package.json index 415982dc2..a74fe4134 100644 --- a/tracker/tracker-graphql/package.json +++ b/tracker/tracker-graphql/package.json @@ -1,7 +1,7 @@ { "name": "@openreplay/tracker-graphql", "description": "Tracker plugin for GraphQL requests recording", - "version": "3.0.1", + "version": "4.0.1-2", "keywords": [ "graphql", "logging", @@ -22,12 +22,17 @@ "prepublishOnly": "npm run build" }, "peerDependencies": { - "@openreplay/tracker": ">=3.0.0" + "@openreplay/tracker": ">=13.0.0" }, "devDependencies": { "@openreplay/tracker": "file:../tracker", "prettier": "^1.18.2", "replace-in-files-cli": "^1.0.0", - "typescript": "^3.6.4" + "typescript": "^5.3.3" + }, + "dependencies": { + "@apollo/client": "^3.9.5", + "@types/zen-observable": "^0.8.7", + "zen-observable": "^0.10.0" } } diff --git a/tracker/tracker-graphql/src/apolloMiddleware.ts b/tracker/tracker-graphql/src/apolloMiddleware.ts new file mode 100644 index 000000000..948186b77 --- /dev/null +++ b/tracker/tracker-graphql/src/apolloMiddleware.ts @@ -0,0 +1,57 @@ +import { App, Messages } from '@openreplay/tracker'; +import Observable from 'zen-observable'; + +type Operation = { + query: Record; + variables: Record; + operationName: string; + extensions: Record; +}; +type NextLink = (operation: Operation) => Observable>; + +export const createTrackerLink = (app: App | null) => { + if (!app) { + return (operation: Operation, forward: NextLink) => forward(operation); + } + return (operation: Operation, forward: NextLink) => { + return new Observable((observer) => { + const start = app.timestamp(); + const observable = forward(operation); + const subscription = observable.subscribe({ + next(value) { + const end = app.timestamp(); + app.send( + Messages.GraphQL( + operation.query.definitions[0].kind, + operation.operationName, + JSON.stringify(operation.variables), + JSON.stringify(value.data), + end - start, + ), + ); + observer.next(value); + }, + error(error) { + const end = app.timestamp(); + app.send( + Messages.GraphQL( + operation.query.definitions[0].kind, + operation.operationName, + JSON.stringify(operation.variables), + JSON.stringify(error), + end - start, + ), + ); + observer.error(error); + }, + complete() { + observer.complete(); + }, + }); + + return () => subscription.unsubscribe(); + }); + }; +}; + +export default createTrackerLink; diff --git a/tracker/tracker-graphql/src/graphqlMiddleware.ts b/tracker/tracker-graphql/src/graphqlMiddleware.ts new file mode 100644 index 000000000..e5302d232 --- /dev/null +++ b/tracker/tracker-graphql/src/graphqlMiddleware.ts @@ -0,0 +1,33 @@ +import { App, Messages } from "@openreplay/tracker"; + +function createGraphqlMiddleware() { + return (app: App | null) => { + if (app === null) { + return (_1: string, _2: string, _3: any, result: any) => result; + } + return ( + operationKind: string, + operationName: string, + variables: any, + result: any, + duration = 0 + ) => { + try { + app.send( + Messages.GraphQL( + operationKind, + operationName, + JSON.stringify(variables), + JSON.stringify(result), + duration, + ), + ); + } catch (e) { + console.error(e); + } + return result; + }; + }; +} + +export default createGraphqlMiddleware \ No newline at end of file diff --git a/tracker/tracker-graphql/src/index.ts b/tracker/tracker-graphql/src/index.ts index afb878cfe..339836352 100644 --- a/tracker/tracker-graphql/src/index.ts +++ b/tracker/tracker-graphql/src/index.ts @@ -1,28 +1,9 @@ -import { App, Messages } from '@openreplay/tracker'; +import createTrackerLink from './apolloMiddleware.js'; +import createRelayMiddleware from './relayMiddleware.js'; +import createGraphqlMiddleware from './graphqlMiddleware.js'; -export default function() { - return (app: App | null) => { - if (app === null) { - return (_1: string, _2: string, _3: any, result: any) => result; - } - return ( - operationKind: string, - operationName: string, - variables: any, - result: any, - ) => { - try { - app.send( - Messages.GraphQL( - operationKind, - operationName, - JSON.stringify(variables), - JSON.stringify(result), - ), - ); - } finally { - return result; - } - }; - }; -} +export { + createTrackerLink, + createRelayMiddleware, + createGraphqlMiddleware, +} \ No newline at end of file diff --git a/tracker/tracker-graphql/src/relayMiddleware.ts b/tracker/tracker-graphql/src/relayMiddleware.ts new file mode 100644 index 000000000..f1e9cc721 --- /dev/null +++ b/tracker/tracker-graphql/src/relayMiddleware.ts @@ -0,0 +1,37 @@ +import { App, Messages } from '@openreplay/tracker'; +import type { Middleware, RelayRequest } from './relaytypes'; + +const createRelayMiddleware = (app: App | null): Middleware => { + if (!app) { + return (next) => async (req) => await next(req); + } + return (next) => async (req) => { + const start = app.timestamp(); + const resp = await next(req) + const end = app.timestamp(); + if ('requests' in req) { + req.requests.forEach((request) => { + app.send(getMessage(request, resp.json as Record, end - start)) + }) + } else { + app.send(getMessage(req, resp.json as Record, end - start)) + } + return resp; + } +}; + +function getMessage(request: RelayRequest, json: Record, duration: number) { + const opKind = request.operation.kind; + const opName = request.operation.name; + const vars = JSON.stringify(request.variables) + const opResp = JSON.stringify(json) + return Messages.GraphQL( + opKind, + opName, + vars, + opResp, + duration + ) +} + +export default createRelayMiddleware diff --git a/tracker/tracker-graphql/src/relaytypes.ts b/tracker/tracker-graphql/src/relaytypes.ts new file mode 100644 index 000000000..4330918d5 --- /dev/null +++ b/tracker/tracker-graphql/src/relaytypes.ts @@ -0,0 +1,85 @@ + +type ConcreteBatch = { + kind: 'Batch'; + fragment: any; + id: string | null; + metadata: { [key: string]: any }; + name: string; + query: any; + text: string | null; + operationKind: string; +}; +type Variables = { [name: string]: any }; +interface FetchOpts { + url?: string; + method: 'POST' | 'GET'; + headers: Headers; + body: string | FormData; + credentials?: 'same-origin' | 'include' | 'omit'; + mode?: 'cors' | 'websocket' | 'navigate' | 'no-cors' | 'same-origin'; + cache?: 'default' | 'no-store' | 'reload' | 'no-cache' | 'force-cache' | 'only-if-cached'; + redirect?: 'follow' | 'error' | 'manual'; + signal?: AbortSignal; + [name: string]: any; +} +export interface RelayRequest { + lastGenId: number; + id: string; + fetchOpts: FetchOpts; + + operation: ConcreteBatch; + variables: Variables; + controller: AbortController | null; + + getBody(): string | FormData; + prepareBody(): string | FormData; + getID(): string; + getQueryString(): string; + getVariables(): Variables; + isMutation(): boolean; + isFormData(): boolean; + cancel(): boolean; + clone(): RelayRequest; +} + +declare class RelayRequestBatch { + requests: RelayRequest[]; + + setFetchOption(name: string, value: any): void; + setFetchOptions(opts: {}): void; + getBody(): string; + prepareBody(): string; + getIds(): string[]; + getID(): string; + isMutation(): boolean; + isFormData(): boolean; + clone(): RelayRequestBatch; + getQueryString(): string; +} +type GraphQLResponseErrors = Array<{ + message: string; + locations?: [{ column: number; line: number }]; + stack?: string[]; +}>; +export interface RelayResponse { + _res: any; + + data?: Record; + errors?: GraphQLResponseErrors; + + ok: any; + status: number; + statusText?: string; + headers?: Headers; + url?: string; + text?: string; + json: unknown; + + processJsonData(json: unknown): void; + clone(): RelayResponse; + toString(): string; +} + +export type RelayRequestAny = RelayRequest | RelayRequestBatch; +export type MiddlewareNextFn = (req: RelayRequestAny) => Promise; +export type Middleware = (next: MiddlewareNextFn) => MiddlewareNextFn; diff --git a/tracker/tracker-graphql/tsconfig.json b/tracker/tracker-graphql/tsconfig.json index ce07a685b..31699449b 100644 --- a/tracker/tracker-graphql/tsconfig.json +++ b/tracker/tracker-graphql/tsconfig.json @@ -7,6 +7,7 @@ "module": "es6", "moduleResolution": "node", "declaration": true, - "outDir": "./lib" + "outDir": "./lib", + "allowSyntheticDefaultImports": true } } diff --git a/tracker/tracker/CHANGELOG.md b/tracker/tracker/CHANGELOG.md index 15298ce84..8bc8481fa 100644 --- a/tracker/tracker/CHANGELOG.md +++ b/tracker/tracker/CHANGELOG.md @@ -1,3 +1,7 @@ +# 15.0.0 + +- updated graphql plugin and messages + # 14.0.0 - titles for tabs diff --git a/tracker/tracker/src/common/messages.gen.ts b/tracker/tracker/src/common/messages.gen.ts index 73c66f16e..7ec02abe7 100644 --- a/tracker/tracker/src/common/messages.gen.ts +++ b/tracker/tracker/src/common/messages.gen.ts @@ -37,7 +37,7 @@ export declare const enum Type { Vuex = 45, MobX = 46, NgRx = 47, - GraphQL = 48, + GraphQLDeprecated = 48, PerformanceTrack = 49, StringDict = 50, SetNodeAttributeDict = 51, @@ -77,6 +77,7 @@ export declare const enum Type { TagTrigger = 120, Redux = 121, SetPageLocation = 122, + GraphQL = 123, } @@ -317,12 +318,13 @@ export type NgRx = [ /*duration:*/ number, ] -export type GraphQL = [ - /*type:*/ Type.GraphQL, +export type GraphQLDeprecated = [ + /*type:*/ Type.GraphQLDeprecated, /*operationKind:*/ string, /*operationName:*/ string, /*variables:*/ string, /*response:*/ string, + /*duration:*/ number, ] export type PerformanceTrack = [ @@ -615,6 +617,15 @@ export type SetPageLocation = [ /*documentTitle:*/ string, ] +export type GraphQL = [ + /*type:*/ Type.GraphQL, + /*operationKind:*/ string, + /*operationName:*/ string, + /*variables:*/ string, + /*response:*/ string, + /*duration:*/ number, +] -type Message = Timestamp | SetPageLocationDeprecated | SetViewportSize | SetViewportScroll | CreateDocument | CreateElementNode | CreateTextNode | MoveNode | RemoveNode | SetNodeAttribute | RemoveNodeAttribute | SetNodeData | SetNodeScroll | SetInputTarget | SetInputValue | SetInputChecked | MouseMove | NetworkRequestDeprecated | ConsoleLog | PageLoadTiming | PageRenderTiming | CustomEvent | UserID | UserAnonymousID | Metadata | CSSInsertRule | CSSDeleteRule | Fetch | Profiler | OTable | StateAction | ReduxDeprecated | Vuex | MobX | NgRx | GraphQL | PerformanceTrack | StringDict | SetNodeAttributeDict | ResourceTimingDeprecated | ConnectionInformation | SetPageVisibility | LoadFontFace | SetNodeFocus | LongTask | SetNodeAttributeURLBased | SetCSSDataURLBased | TechnicalInfo | CustomIssue | CSSInsertRuleURLBased | MouseClick | MouseClickDeprecated | CreateIFrameDocument | AdoptedSSReplaceURLBased | AdoptedSSInsertRuleURLBased | AdoptedSSDeleteRule | AdoptedSSAddOwner | AdoptedSSRemoveOwner | JSException | Zustand | BatchMetadata | PartitionedMessage | NetworkRequest | WSChannel | InputChange | SelectionChange | MouseThrashing | UnbindNodes | ResourceTiming | TabChange | TabData | CanvasNode | TagTrigger | Redux | SetPageLocation + +type Message = Timestamp | SetPageLocationDeprecated | SetViewportSize | SetViewportScroll | CreateDocument | CreateElementNode | CreateTextNode | MoveNode | RemoveNode | SetNodeAttribute | RemoveNodeAttribute | SetNodeData | SetNodeScroll | SetInputTarget | SetInputValue | SetInputChecked | MouseMove | NetworkRequestDeprecated | ConsoleLog | PageLoadTiming | PageRenderTiming | CustomEvent | UserID | UserAnonymousID | Metadata | CSSInsertRule | CSSDeleteRule | Fetch | Profiler | OTable | StateAction | ReduxDeprecated | Vuex | MobX | NgRx | GraphQLDeprecated | PerformanceTrack | StringDict | SetNodeAttributeDict | ResourceTimingDeprecated | ConnectionInformation | SetPageVisibility | LoadFontFace | SetNodeFocus | LongTask | SetNodeAttributeURLBased | SetCSSDataURLBased | TechnicalInfo | CustomIssue | CSSInsertRuleURLBased | MouseClick | MouseClickDeprecated | CreateIFrameDocument | AdoptedSSReplaceURLBased | AdoptedSSInsertRuleURLBased | AdoptedSSDeleteRule | AdoptedSSAddOwner | AdoptedSSRemoveOwner | JSException | Zustand | BatchMetadata | PartitionedMessage | NetworkRequest | WSChannel | InputChange | SelectionChange | MouseThrashing | UnbindNodes | ResourceTiming | TabChange | TabData | CanvasNode | TagTrigger | Redux | SetPageLocation | GraphQL export default Message diff --git a/tracker/tracker/src/main/app/messages.gen.ts b/tracker/tracker/src/main/app/messages.gen.ts index 60e36ca38..a951a6148 100644 --- a/tracker/tracker/src/main/app/messages.gen.ts +++ b/tracker/tracker/src/main/app/messages.gen.ts @@ -444,18 +444,20 @@ export function NgRx( ] } -export function GraphQL( +export function GraphQLDeprecated( operationKind: string, operationName: string, variables: string, response: string, -): Messages.GraphQL { + duration: number, +): Messages.GraphQLDeprecated { return [ - Messages.Type.GraphQL, + Messages.Type.GraphQLDeprecated, operationKind, operationName, variables, response, + duration, ] } @@ -1000,3 +1002,20 @@ export function SetPageLocation( ] } +export function GraphQL( + operationKind: string, + operationName: string, + variables: string, + response: string, + duration: number, +): Messages.GraphQL { + return [ + Messages.Type.GraphQL, + operationKind, + operationName, + variables, + response, + duration, + ] +} + diff --git a/tracker/tracker/src/webworker/MessageEncoder.gen.ts b/tracker/tracker/src/webworker/MessageEncoder.gen.ts index e1d294fac..bec1496be 100644 --- a/tracker/tracker/src/webworker/MessageEncoder.gen.ts +++ b/tracker/tracker/src/webworker/MessageEncoder.gen.ts @@ -150,8 +150,8 @@ export default class MessageEncoder extends PrimitiveEncoder { return this.string(msg[1]) && this.string(msg[2]) && this.uint(msg[3]) break - case Messages.Type.GraphQL: - return this.string(msg[1]) && this.string(msg[2]) && this.string(msg[3]) && this.string(msg[4]) + case Messages.Type.GraphQLDeprecated: + return this.string(msg[1]) && this.string(msg[2]) && this.string(msg[3]) && this.string(msg[4]) && this.int(msg[5]) break case Messages.Type.PerformanceTrack: @@ -310,6 +310,10 @@ export default class MessageEncoder extends PrimitiveEncoder { return this.string(msg[1]) && this.string(msg[2]) && this.uint(msg[3]) && this.string(msg[4]) break + case Messages.Type.GraphQL: + return this.string(msg[1]) && this.string(msg[2]) && this.string(msg[3]) && this.string(msg[4]) && this.uint(msg[5]) + break + } }