Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add types and remove headers logging #18

Merged
merged 7 commits into from
Apr 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 15 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,21 @@ A very simple logger for Apollo Server

### yarn

``` bash
```bash
yarn add @est-normalis/simple-apollo-logger
```

### npm

``` bash
```bash
npm i @est-normalis/simple-apollo-logger
```

## Usage

To use this package you need to add extension to your ApolloServer

``` typescript
```typescript
[...]
import logger from '@est-normalis/simple-apollo-logger'

Expand All @@ -41,7 +41,7 @@ Now you will be able to see logs in your console.
Simple-apollo-logger is highly customizable. You can pass options to it
when creating it's object.

``` typescript
```typescript
[...]

const opts = {
Expand Down Expand Up @@ -85,7 +85,7 @@ It is using recursive search inside object to find even nested variables with ma

It is the default filter included in this extension:

``` typescript
```typescript
variableFilter: {
keywords: ["password"],
replacementText: "[FILTERED]"
Expand Down Expand Up @@ -118,32 +118,38 @@ Example:

```typescript
const opts = {
logger: customLogger // customLogger has .log() method
logger: customLogger // customLogger has .log() method
}
```

0.3.x:

```typescript
const opts = {
logger: (msg) => customLogger.log(msg)
logger: msg => customLogger.log(msg)
}
```

If you were not using custom logger this update should not make any major changes.

#### 0.3.x to 0.4.x

This update should not result in major changes except for not logging headers anymore [reson](https://github.com/est-normalis/simple-apollo-logger/pull/18).
In this update TypeScript type definitions were also added (they replaced `any` type in `requestDidStart` function), but it should not
change way of how is the logger working.

##### Prefix

Default prefix was changed from:

```typescript
`[${Date.now()}]`
;`[${Date.now()}]`
```

to:

```typescript
`[${Date.now()}] `
;`[${Date.now()}] `
```

Output from logger with default options should remain the same,
Expand Down
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@est-normalis/simple-apollo-logger",
"version": "0.3.1",
"version": "0.4.0",
"description": "A simple logger for Apollo Server",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down Expand Up @@ -29,6 +29,10 @@
"devDependencies": {
"@types/jest": "^25.2.1",
"@types/node": "^13.13.0",
"apollo-server-env": "^2.4.3",
"apollo-server-types": "^0.3.1",
"graphql": "^15.0.0",
"graphql-extensions": "^0.11.1",
"jest": "^25.3.0",
"prettier": "^2.0.4",
"ts-jest": "^25.4.0",
Expand Down
15 changes: 7 additions & 8 deletions src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
import { GraphQLExtension } from 'graphql-extensions'
import { stringifiedRequestAttributes } from './formatting'
import { defaultOptions, Options, UserOptions } from './options'
import { ApolloRequest } from './types'

export default class ApolloLogExtension {
export default class ApolloLogExtension<TContext = any>
implements GraphQLExtension<TContext> {
private options: Options

constructor(ops: UserOptions = {}) {
this.options = Object.assign(defaultOptions, ops)
}

// todo: pick a right interface to match both apollo extension
// requirements and internal formating requirements
public requestDidStart(request: any): void {
const isInspectionQuery = request.operationName === 'IntrospectionQuery'
public requestDidStart(r: ApolloRequest): void {
const isInspectionQuery = r.operationName === 'IntrospectionQuery'
const isIgnoredRequest =
this.options.ignoreSchemaRequest && isInspectionQuery
const shouldBeLogged = this.options.logRequests && !isIgnoredRequest
if (shouldBeLogged) {
this.log(
stringifiedRequestAttributes(request, this.options.variableFilter)
)
this.log(stringifiedRequestAttributes(r, this.options.variableFilter))
}
}

Expand Down
12 changes: 2 additions & 10 deletions src/formatting.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import deepReplace from './helpers/deepReplace'
import { ApolloRequest } from './types'

const filterPasswordFromVariables = (
variables: any,
Expand All @@ -12,7 +13,7 @@ const filterPasswordFromVariables = (
}

export const stringifiedRequestAttributes = (
{ headers, variables, queryString, operationName }: Request,
{ variables, queryString, operationName }: ApolloRequest,
variableFilter: VariableFilter | undefined | false
): string => {
const copyOfVariables = JSON.parse(JSON.stringify(variables))
Expand All @@ -26,26 +27,17 @@ export const stringifiedRequestAttributes = (
})
}

const stringifiedHeaders = JSON.stringify(headers)
const stringifiedVariables = JSON.stringify(copyOfVariables)
const stringifiedQueryString = JSON.stringify(queryString)
.replace(/\s/g, '')
.replace(/\\n/g, ' ')

return `Request started
Operation name: ${operationName}
Headers: ${stringifiedHeaders}
QueryString: ${stringifiedQueryString}
Variables: ${stringifiedVariables}`
}

export interface Request {
headers: object
queryString: any
operationName: string
variables: object
}

export interface VariableFilter {
keywords: string[]
replacementText: string
Expand Down
17 changes: 17 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Request } from 'apollo-server-env'
import { GraphQLRequestContext } from 'apollo-server-types'
import { DocumentNode } from 'graphql'

type TContext = any // workaround since types are in different file than extension class

export interface ApolloRequest {
request: Pick<Request, 'url' | 'method' | 'headers'>
queryString?: string
parsedQuery?: DocumentNode
operationName?: string
variables?: { [key: string]: any }
persistedQueryHit?: boolean
persistedQueryRegister?: boolean
context: TContext
requestContext: GraphQLRequestContext<TContext>
}
3 changes: 0 additions & 3 deletions test/__snapshots__/request.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ exports[`requestDidStart function when logging requests is enabled introspection
Array [
"[1600689779135] Request started
Operation name: IntrospectionQuery
Headers: {\\"Authorization\\":\\"eyyyyLMAO.dd.json\\"}
QueryString: \\"queryIntrospectionQuery{ __schema{ queryType{ name } mutationType{ name } subscriptionType{ name } types{ ...FullType } directives{ name description locations args{ ...InputValue } } } } fragmentFullTypeon__Type{ kind name description fields(includeDeprecated:true){ name description args{ ...InputValue } type{ ...TypeRef } isDeprecated deprecationReason } inputFields{ ...InputValue } interfaces{ ...TypeRef } enumValues(includeDeprecated:true){ name description isDeprecated deprecationReason } possibleTypes{ ...TypeRef } } fragmentInputValueon__InputValue{ name description type{ ...TypeRef } defaultValue } fragmentTypeRefon__Type{ kind name ofType{ kind name ofType{ kind name ofType{ kind name ofType{ kind name ofType{ kind name ofType{ kind name ofType{ kind name } } } } } } } } \\"
Variables: {}",
],
Expand All @@ -26,14 +25,12 @@ exports[`requestDidStart function when logging requests is enabled login mutatio
Array [
"[1600689779135] Request started
Operation name: IntrospectionQuery
Headers: {\\"Authorization\\":\\"eyyyyLMAO.dd.json\\"}
QueryString: \\"queryIntrospectionQuery{ __schema{ queryType{ name } mutationType{ name } subscriptionType{ name } types{ ...FullType } directives{ name description locations args{ ...InputValue } } } } fragmentFullTypeon__Type{ kind name description fields(includeDeprecated:true){ name description args{ ...InputValue } type{ ...TypeRef } isDeprecated deprecationReason } inputFields{ ...InputValue } interfaces{ ...TypeRef } enumValues(includeDeprecated:true){ name description isDeprecated deprecationReason } possibleTypes{ ...TypeRef } } fragmentInputValueon__InputValue{ name description type{ ...TypeRef } defaultValue } fragmentTypeRefon__Type{ kind name ofType{ kind name ofType{ kind name ofType{ kind name ofType{ kind name ofType{ kind name ofType{ kind name ofType{ kind name } } } } } } } } \\"
Variables: {}",
],
Array [
"[1600689779135] Request started
Operation name: null
Headers: undefined
QueryString: \\"mutation($input:SignupInput!){ signup(input:$input){ token } } \\"
Variables: {\\"input\\":{\\"email\\":\\"ddddddddassaaaaad@dddd.dde\\",\\"password\\":\\"[FILTERED]\\",\\"name\\":\\"ddoopson\\"}}",
],
Expand Down
17 changes: 16 additions & 1 deletion test/data/requests/introspection.json
Original file line number Diff line number Diff line change
@@ -1 +1,16 @@
{"queryString":"query IntrospectionQuery {\n __schema {\n queryType {\n name\n }\n mutationType {\n name\n }\n subscriptionType {\n name\n }\n types {\n ...FullType\n }\n directives {\n name\n description\n locations\n args {\n ...InputValue\n }\n }\n }\n}\n\nfragment FullType on __Type {\n kind\n name\n description\n fields(includeDeprecated: true) {\n name\n description\n args {\n ...InputValue\n }\n type {\n ...TypeRef\n }\n isDeprecated\n deprecationReason\n }\n inputFields {\n ...InputValue\n }\n interfaces {\n ...TypeRef\n }\n enumValues(includeDeprecated: true) {\n name\n description\n isDeprecated\n deprecationReason\n }\n possibleTypes {\n ...TypeRef\n }\n}\n\nfragment InputValue on __InputValue {\n name\n description\n type {\n ...TypeRef\n }\n defaultValue\n}\n\nfragment TypeRef on __Type {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n }\n }\n }\n }\n }\n }\n }\n}\n","operationName":"IntrospectionQuery","variables":{}, "headers":{"Authorization": "eyyyyLMAO.dd.json"}}
{
"queryString": "query IntrospectionQuery {\n __schema {\n queryType {\n name\n }\n mutationType {\n name\n }\n subscriptionType {\n name\n }\n types {\n ...FullType\n }\n directives {\n name\n description\n locations\n args {\n ...InputValue\n }\n }\n }\n}\n\nfragment FullType on __Type {\n kind\n name\n description\n fields(includeDeprecated: true) {\n name\n description\n args {\n ...InputValue\n }\n type {\n ...TypeRef\n }\n isDeprecated\n deprecationReason\n }\n inputFields {\n ...InputValue\n }\n interfaces {\n ...TypeRef\n }\n enumValues(includeDeprecated: true) {\n name\n description\n isDeprecated\n deprecationReason\n }\n possibleTypes {\n ...TypeRef\n }\n}\n\nfragment InputValue on __InputValue {\n name\n description\n type {\n ...TypeRef\n }\n defaultValue\n}\n\nfragment TypeRef on __Type {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n }\n }\n }\n }\n }\n }\n }\n}\n",
"operationName": "IntrospectionQuery",
"variables": {},
"request": {
"size": 0,
"timeout": 0,
"follow": 20,
"compress": true,
"counter": 0
},
"persistedQueryHit": false,
"persistedQueryRegister": false,
"context": {},
"requestContext": {}
}
23 changes: 22 additions & 1 deletion test/data/requests/login.json
Original file line number Diff line number Diff line change
@@ -1 +1,22 @@
{"queryString":"mutation ($input: SignupInput!) {\n signup(input: $input) {\n token\n }\n}\n","operationName":null,"variables":{"input":{"email":"ddddddddassaaaaad@dddd.dde","password":"dddddddddddddddd","name":"ddoopson"}}}
{
"queryString": "mutation ($input: SignupInput!) {\n signup(input: $input) {\n token\n }\n}\n",
"operationName": null,
"variables": {
"input": {
"email": "ddddddddassaaaaad@dddd.dde",
"password": "dddddddddddddddd",
"name": "ddoopson"
}
},
"request": {
"size": 0,
"timeout": 0,
"follow": 20,
"compress": true,
"counter": 0
},
"persistedQueryHit": false,
"persistedQueryRegister": false,
"context": {},
"requestContext": {}
}
Loading