Browse Source

Feature: Mobile - Improved apollo client debug link for the graphql operations.

Dominik Klein 3 years ago
parent
commit
0108ea4825

+ 52 - 9
app/frontend/common/server/apollo/link/debug.ts

@@ -1,28 +1,71 @@
 // Copyright (C) 2012-2021 Zammad Foundation, https://zammad-foundation.org/
 
 import { ApolloLink } from '@apollo/client/core'
+import { getMainDefinition } from '@apollo/client/utilities'
+import {
+  DebugLinkRequestOutput,
+  DebugLinkResponseOutput,
+} from '@common/types/server/apollo/client'
 import log from '@common/utils/log'
+import { print } from 'graphql/language/printer'
+import { capitalize, isEmpty } from 'lodash-es'
 
 const debugLink = new ApolloLink((operation, forward) => {
   if (log.getLevel() < log.levels.DEBUG) return forward(operation)
 
-  // TODO: add maybe also a time tracking for request->response time.
+  const requestContext = operation.getContext()
+
+  const definition = getMainDefinition(operation.query)
+  const opeationType =
+    definition.kind === 'OperationDefinition'
+      ? capitalize(definition.operation)
+      : null
+
+  const requestOutput: DebugLinkRequestOutput = {
+    printedDocument: print(operation.query),
+    document: operation.query,
+  }
+
+  if (!isEmpty(operation.variables)) {
+    requestOutput.variables = operation.variables
+  }
+
+  if (!isEmpty(requestContext.headers)) {
+    requestOutput.requestHeaders = requestContext.headers
+  }
 
   // Called before operation is sent to server
-  // operation.setContext({ start: new Date() })
+  operation.setContext({ start: new Date() })
+
   log.debug(
-    `[GraphQL - Request] - ${operation.operationName}:`,
-    operation.getContext(),
+    `[GraphQL - Request] - ${opeationType} - ${operation.operationName}:`,
+    requestOutput,
   )
 
   return forward(operation).map((data) => {
-    // Called after server responds
-    // const end = new Date()
+    const context = operation.getContext()
+    const end = new Date()
+
+    const responseHeaders: Record<string, string> = {}
+    if (context?.response?.headers) {
+      context.response.headers.forEach((value: string, key: string) => {
+        responseHeaders[key] = value
+      })
+    }
+
+    const duration = end.getTime() - context.start.getTime()
+
+    const responseOutput: DebugLinkResponseOutput = {
+      data,
+    }
+
+    if (!isEmpty(responseHeaders)) {
+      responseOutput.responseHeaders = responseHeaders
+    }
 
-    // const time = end - operation.getContext().start
     log.debug(
-      `[GraphQL - Response] - ${operation.operationName}:`,
-      operation.getContext(),
+      `[GraphQL - Response] - ${opeationType} - ${operation.operationName} (took ${duration}ms):`,
+      responseOutput,
     )
     return data
   })

+ 3 - 4
app/frontend/common/server/apollo/utils/getErrorContext.ts

@@ -3,13 +3,12 @@
 import { Operation } from '@apollo/client/core'
 import { ClientErrorContext } from '@common/types/server/apollo/client'
 
-const defaultErrorContext: ClientErrorContext = {
-  logLevel: 'error',
-}
-
 export default function getErrorContext(
   operation: Operation,
 ): ClientErrorContext {
+  const defaultErrorContext: ClientErrorContext = {
+    logLevel: 'error',
+  }
   const context = operation.getContext()
   const error: Partial<ClientErrorContext> = context.error || {}
 

+ 15 - 0
app/frontend/common/types/server/apollo/client.ts

@@ -1,5 +1,20 @@
 // Copyright (C) 2012-2021 Zammad Foundation, https://zammad-foundation.org/
 
+import { FetchResult } from '@apollo/client/core'
+import { DocumentNode } from 'graphql'
+
 export interface ClientErrorContext {
   logLevel: LogLevel
 }
+
+export interface DebugLinkRequestOutput {
+  requestHeaders?: Record<string, string>
+  printedDocument: string
+  document: DocumentNode
+  variables?: Record<string, unknown>
+}
+
+export interface DebugLinkResponseOutput {
+  data: FetchResult
+  responseHeaders?: Record<string, string>
+}

+ 12 - 12
vite.config.ts

@@ -3,8 +3,8 @@
 import { defineConfig } from 'vite'
 import RubyPlugin from 'vite-plugin-ruby'
 import VuePlugin from '@vitejs/plugin-vue'
-import viteSvgIcons from 'vite-plugin-svg-icons';
-import type { OptimizeOptions } from 'svgo';
+import viteSvgIcons from 'vite-plugin-svg-icons'
+import type { OptimizeOptions } from 'svgo'
 import * as path from 'path'
 
 export default defineConfig({
@@ -24,28 +24,28 @@ export default defineConfig({
       symbolId: 'icon-[dir]-[name]',
       svgoOptions: {
         plugins: [
-          {name: 'preset-default'},
+          { name: 'preset-default' },
           {
-            name: "removeAttributesBySelector",
+            name: 'removeAttributesBySelector',
             params: {
               selectors: [
                 {
                   selector: "[fill='#50E3C2']",
-                  attributes: "fill"
+                  attributes: 'fill',
                 },
                 {
                   selector: "[fill='#BD0FE1']",
-                  attributes: "fill"
+                  attributes: 'fill',
                 },
                 {
                   selector: "[fill='#BD10E0']",
-                  attributes: "fill"
-                }
-              ]
-            } 
-          }
+                  attributes: 'fill',
+                },
+              ],
+            },
+          },
         ],
-      } as OptimizeOptions
+      } as OptimizeOptions,
     }),
   ],
 })