Skip to content

Commit

Permalink
feat(directive): add @purgeCache & @logCache directives
Browse files Browse the repository at this point in the history
  • Loading branch information
robertu7 committed Jul 22, 2020
1 parent 2204719 commit 4e74523
Show file tree
Hide file tree
Showing 9 changed files with 2,272 additions and 139 deletions.
13 changes: 7 additions & 6 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ module.exports = {
'prettier',
],
rules: {
"camelcase": "off",
"@typescript-eslint/camelcase": "off",
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-non-null-assertion": "off"
}
camelcase: 'off',
'@typescript-eslint/camelcase': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-unused-vars': "off",
},
}
2,275 changes: 2,147 additions & 128 deletions package-lock.json

Large diffs are not rendered by default.

10 changes: 8 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,17 @@
"dependencies": {
"apollo-cache-control": "^0.11.1",
"apollo-server-caching": "^0.5.2",
"apollo-server-plugin-base": "^0.9.1"
"apollo-server-plugin-base": "^0.9.1",
"graphql": "^15.3.0",
"graphql-tools": "^6.0.14",
"lodash": "^4.17.19"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^14.0.0",
"@rollup/plugin-node-resolve": "^8.4.0",
"@rollup/plugin-typescript": "^5.0.2",
"@types/graphql": "^14.5.0",
"@types/lodash": "^4.14.158",
"@typescript-eslint/eslint-plugin": "^3.7.0",
"@typescript-eslint/parser": "^3.7.0",
"apollo-server-types": "^0.5.1",
Expand All @@ -52,7 +56,9 @@
"typescript": "^3.9.7"
},
"peerDependencies": {
"graphql": "^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0"
"graphql": "^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0",
"graphql-tools": "^6.0.0",
"lodash": "^4.0.0"
},
"files": [
"dist/**/*"
Expand Down
37 changes: 37 additions & 0 deletions src/directives/logCache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { defaultFieldResolver, GraphQLField } from 'graphql'
import { SchemaDirectiveVisitor } from 'graphql-tools'

import { CACHE_PREFIX } from '../enums'

interface Params {
_type: string
}

export class LogCacheDirective extends SchemaDirectiveVisitor {
visitFieldDefinition(field: GraphQLField<any, any> & Params): void {
const { resolve = defaultFieldResolver } = field
field._type = this.args.type
field.resolve = async function (...args) {
const [root, _, { cacheKeys, redis }] = args
const result = await resolve.apply(this, args)

if (result && result.id && redis && cacheKeys && field._type) {
try {
let cacheType = field._type
switch (field._type) {
case 'Node':
case 'Response': {
cacheType = result.__type
break
}
}
cacheKeys.add(`${CACHE_PREFIX.KEYS}:${cacheType}:${result.id}`)
} catch (error) {
// TOOD: logger
}
}

return result
}
}
}
59 changes: 59 additions & 0 deletions src/directives/purgeCache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { defaultFieldResolver, GraphQLField } from 'graphql'
import { SchemaDirectiveVisitor } from 'graphql-tools'
import { compact, replace, get } from 'lodash'

import { CACHE_KEYWORD, CACHE_PREFIX } from '../enums'

interface CacheSet {
id: string
type: string
}

const getCacheKeys = (customs: CacheSet[], fallback: CacheSet): string[] => {
if (customs && customs.length > 0) {
return compact(
customs.map((custom: CacheSet) => {
if (custom && custom.id && custom.type) {
return `${CACHE_PREFIX.KEYS}:${custom.type}:${custom.id}`
}
})
)
}

return [
`${CACHE_PREFIX.KEYS}:${replace(fallback.type, '!', '')}:${fallback.id}`,
]
}

export class PurgeCacheDirective extends SchemaDirectiveVisitor {
visitFieldDefinition(field: GraphQLField<any, any>): void {
const { resolve = defaultFieldResolver } = field
field.resolve = async function (...args) {
const [root, _, { redis }, { returnType }] = args

const result = await resolve.apply(this, args)
if (result && result.id && redis && returnType) {
try {
const cache = get(result, CACHE_KEYWORD, [])
const keys = getCacheKeys(cache, {
id: result.id,
type: `${returnType}`,
})
keys.map(async (key: string) => {
const hashes = await redis.client.smembers(key)
hashes.map(async (hash: string) => {
await redis.client
.pipeline()
.del(`fqc:${hash}`)
.srem(key, hash)
.exec()
})
})
} catch (error) {
// TODO: logger
}
}
return result
}
}
}
7 changes: 7 additions & 0 deletions src/enums.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// keyword notating for cache invalidation
export const CACHE_KEYWORD = '__cache__'

// redis cache for apq keys or resolver returned objects
export const CACHE_PREFIX = {
KEYS: 'cache-keys',
}
6 changes: 4 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import responseCachePlugin from './ApolloServerPluginResponseCache'
import responseCachePlugin from './plugins/responseCachePlugin'
import { LogCacheDirective } from './directives/logCache'
import { PurgeCacheDirective } from './directives/purgeCache'

export { responseCachePlugin }
export { responseCachePlugin, LogCacheDirective, PurgeCacheDirective }
File renamed without changes.
4 changes: 3 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{
"compilerOptions": {},
"compilerOptions": {
"esModuleInterop": true
},
"exclude": ["dist", "node_modules"]
}

0 comments on commit 4e74523

Please sign in to comment.