Skip to content
This repository has been archived by the owner on Nov 20, 2024. It is now read-only.

Commit

Permalink
feat: add hook for karbon client
Browse files Browse the repository at this point in the history
  • Loading branch information
DanSnow committed Aug 22, 2023
1 parent 381a571 commit 7189b87
Show file tree
Hide file tree
Showing 13 changed files with 1,074 additions and 170 deletions.
5 changes: 3 additions & 2 deletions packages/karbon/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@
"@nuxt/devalue": "^2.0.2",
"@nuxt/image": "^1.0.0-rc.1",
"@nuxt/kit": "^3.6.3",
"@nuxt/schema": "^3.6.5",
"@nuxtjs/html-validator": "^1.5.1",
"@sentry/node": "^7.58.1",
"@sentry/tracing": "^7.58.1",
Expand All @@ -141,6 +142,7 @@
"find-cache-dir": "^4.0.0",
"fs-extra": "^11.1.1",
"graphql": "^16.7.1",
"hookable": "^5.5.3",
"js-yaml": "^4.1.0",
"jszip": "^3.10.1",
"knitwork": "^1.0.0",
Expand Down Expand Up @@ -191,7 +193,6 @@
"@nuxt/eslint-config": "0.1.1",
"@nuxt/kit": "3.6.5",
"@nuxt/module-builder": "0.4.0",
"@nuxt/schema": "3.6.5",
"@nuxt/test-utils": "3.6.5",
"@nuxtjs/tailwindcss": "6.8.0",
"@types/find-cache-dir": "3.2.1",
Expand Down Expand Up @@ -224,4 +225,4 @@
"access": "public"
},
"gitHead": "8df1f4d5837a7e2ddbff6cc79f5fec256c34a394"
}
}
1 change: 1 addition & 0 deletions packages/karbon/src/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export {
createStoripressBaseClient,
storipressConfigCtx,
createTenantURL,
_karbonClientHooks as clientHooks,
} from './runtime/composables/storipress-base-client'
export { definePayloadHandler } from './runtime/routes/payload-handler'
export { getSite } from './runtime/api/site'
Expand Down
21 changes: 20 additions & 1 deletion packages/karbon/src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
addPlugin,
addPrerenderRoutes,
addServerHandler,
addServerPlugin,
addTemplate,
createResolver,
defineNuxtModule,
Expand All @@ -21,7 +22,7 @@ import serialize from 'serialize-javascript'
import fs from 'fs-extra'
import { resolve } from 'pathe'
import { genArrayFromRaw, genImport, genObjectFromRaw } from 'knitwork'
import type { NuxtPlugin } from '@nuxt/schema'
import type { HookResult, NuxtPlugin } from '@nuxt/schema'
import { resolveSEOProviders } from './seo-provider'
import { versionSafe } from './cli/checkFile'
import type {
Expand All @@ -41,6 +42,21 @@ import { getResources, payloadScopes } from './runtime/api/sitemap'
import telemetry from './modules/telemetry'
import feed from './modules/feed'
import instantsearch from './modules/instantsearch'
import type { RequestContext, ResponseContext } from './runtime/composables/storipress-base-client'

declare module '#app' {
interface RuntimeNuxtHooks {
'karbon:request': (ctx: RequestContext) => HookResult
'karbon:response': (ctx: ResponseContext) => HookResult
}
}

declare module 'nitropack' {
interface NitroRuntimeHooks {
'karbon:request': (ctx: RequestContext) => void
'karbon:response': (ctx: ResponseContext) => void
}
}

const AD_COMPONENTS = ['AdvertisingProvider', 'AdvertisingSlot', 'GlobalAdvertisingProvider', 'GlobalAdvertisingSlot']

Expand Down Expand Up @@ -446,6 +462,7 @@ const karbon = defineNuxtModule<ModuleOptions>({
'./runtime/plugins/debug-info.client',
'./runtime/plugins/track.client',
'./runtime/plugins/iframely.client',
'./runtime/plugins/client-hooks',
'./runtime/plugins/1.injectRuntimeConfig',
]

Expand All @@ -465,6 +482,8 @@ const karbon = defineNuxtModule<ModuleOptions>({
)
}

addServerPlugin(resolver.resolve('./runtime/server/plugins/hook'))

await installModule('@nuxt/image', {
provider: 'Storipress',
providers: {
Expand Down
111 changes: 102 additions & 9 deletions packages/karbon/src/runtime/composables/storipress-base-client.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,34 @@
import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client/core/index.js'
import type { Operation } from '@apollo/client/core/index.js'
import { ApolloClient, ApolloLink, HttpLink, InMemoryCache, Observable } from '@apollo/client/core/index.js'
import { setContext } from '@apollo/client/link/context/index.js'
import { fetch } from 'cross-fetch'
import { withHttps } from 'ufo'
import { Hookable } from 'hookable'
import type { Subscription } from 'zen-observable-ts'
import type { ModuleRuntimeConfig } from '../types'

function getUserAgent() {
if (process.server) {
return { 'user-agent': 'karbon/1.0.0' }
}
let c: any = null

return {}
export interface RequestContext {
name: string
id: string
operation: Operation
}

let c: any = null
export interface ResponseContext {
name: string
id: string
operation: Operation
type: 'next' | 'error' | 'complete'
data: any
}

type HookResult = Promise<void> | void

export const _karbonClientHooks = new Hookable<{
'karbon:request': (ctx: RequestContext) => HookResult
'karbon:response': (ctx: ResponseContext) => HookResult
}>()

export const storipressConfigCtx = {
use: () => {
Expand All @@ -37,7 +53,75 @@ export function createTenantURL(config: Pick<ModuleRuntimeConfig['storipress'],
return withHttps(`${config.apiHost}/client/${config.clientId}/graphql`)
}

export function createStoripressBaseClient(getHeaders: () => Record<string, string | null | undefined>, uri: string) {
export interface CreateBaseClientInput {
name?: string
}

export function createStoripressBaseClient(
getHeaders: () => Record<string, string | null | undefined>,
uri: string,
opt: CreateBaseClientInput = {},
) {
const tapClient = new ApolloLink((operation, forward) => {
const id = crypto.randomUUID()

operation.setContext({
karbonTracing: id,
})

return new Observable((observer) => {
let subscription: Subscription
let closed = false
Promise.resolve(
_karbonClientHooks.callHookParallel('karbon:request', {
name: opt.name ?? 'unknown',
operation,
id,
}),
)
.then(() => {
if (closed) {
return
}

function createCallback(type: 'next' | 'error' | 'complete', callback: (...args: any[]) => void) {
return (...args: any[]) => {
Promise.resolve(
_karbonClientHooks.callHookParallel('karbon:response', {
name: opt.name ?? 'unknown',
operation,
id,
type,
data: args[0],
}),
)
.then(() => callback(...args))
.catch((err) => {
if (process.dev) {
console.error(err)
}
})
}
}

subscription = forward(operation).subscribe({
next: createCallback('next', observer.next.bind(observer)),
error: createCallback('error', observer.error.bind(observer)),
complete: createCallback('complete', observer.complete.bind(observer)),
})
})
.catch((err) => {
observer.error(err)
})

return () => {
closed = true
if (subscription) {
subscription.unsubscribe()
}
}
})
})
const authLink = setContext(() => {
return {
headers: { ...getUserAgent(), ...getHeaders() },
Expand All @@ -50,7 +134,7 @@ export function createStoripressBaseClient(getHeaders: () => Record<string, stri
})

return new ApolloClient({
link: authLink.concat(httpLink),
link: ApolloLink.from([authLink, tapClient, httpLink]),
cache: new InMemoryCache({
typePolicies: {
CustomField: {
Expand All @@ -63,3 +147,12 @@ export function createStoripressBaseClient(getHeaders: () => Record<string, stri
}),
})
}

function getUserAgent() {
if (process.server) {
const config = getStoripressConfig()
return { 'user-agent': config.userAgent ?? 'karbon/1.0.0' }
}

return {}
}
3 changes: 2 additions & 1 deletion packages/karbon/src/runtime/composables/storipress-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export function createStoripressClient() {
authorization: `Bearer ${storipress?.apiToken}`,
}),
getUri(),
{ name: 'storipress' },
)
}

Expand All @@ -33,7 +34,7 @@ export function createSubscriberClient() {
}
}

return createStoripressBaseClient(authorization, getUri())
return createStoripressBaseClient(authorization, getUri(), { name: 'storipress-subscriber' })
}

export function getUri() {
Expand Down
12 changes: 12 additions & 0 deletions packages/karbon/src/runtime/plugins/client-hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { _karbonClientHooks } from '../composables/storipress-base-client'
import { defineNuxtPlugin } from '#imports'

export default defineNuxtPlugin((nuxt) => {
_karbonClientHooks.hook('karbon:request', async (ctx) => {
await nuxt.hooks.callHookParallel('karbon:request', ctx)
})

_karbonClientHooks.hook('karbon:response', async (ctx) => {
await nuxt.hooks.callHookParallel('karbon:response', ctx)
})
})
14 changes: 14 additions & 0 deletions packages/karbon/src/runtime/server/plugins/hook.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { clientHooks } from '@storipress/karbon/internal'
import { type _karbonClientHooks } from '../../composables/storipress-base-client'
import { defineNitroPlugin } from '#imports'

const hooks: typeof _karbonClientHooks = clientHooks

export default defineNitroPlugin((nitro) => {
hooks.hook('karbon:request', (ctx) => {
return nitro.hooks.callHookParallel('karbon:request', ctx)
})
hooks.hook('karbon:response', (ctx) => {
return nitro.hooks.callHookParallel('karbon:response', ctx)
})
})
1 change: 1 addition & 0 deletions packages/karbon/src/runtime/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ export interface StoripressRuntimeConfig {
searchKey: string
searchDomain?: string
encryptKey?: string
userAgent?: string
}

export type StoripressPublicRuntimeConfig = Omit<StoripressRuntimeConfig, 'apiToken' | 'stripeKey' | 'encryptKey'>
Expand Down
13 changes: 12 additions & 1 deletion packages/playground/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,17 @@ import {
} from '@storipress/karbon/helper'

export default defineNuxtConfig({
devtools: {
enabled: true,

experimental: {
timeline: true,
},

timeline: {
enabled: true,
},
},
modules: ['@storipress/karbon', '@nuxtjs/tailwindcss', '@vueuse/nuxt'],
build: {
transpile: ['@storipress/vue-advertising'],
Expand Down Expand Up @@ -68,4 +79,4 @@ export default defineNuxtConfig({
logo: './image/favicon.png',
},
},
})
})
1 change: 1 addition & 0 deletions packages/playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
},
"devDependencies": {
"@antfu/eslint-config-vue": "0.40.2",
"@nuxt/devtools": "^0.8.0",
"@nuxt/image": "1.0.0-rc.1",
"@nuxtjs/tailwindcss": "6.8.0",
"@rollup/plugin-virtual": "3.0.1",
Expand Down
9 changes: 9 additions & 0 deletions packages/playground/plugins/listener.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export default defineNuxtPlugin(() => {
const nuxt = useNuxtApp()
nuxt.hooks.hook('karbon:request', (ctx) => {
console.log('nuxt request', ctx)
})
nuxt.hooks.hook('karbon:response', (ctx) => {
console.log('nuxt response', ctx)
})
})
10 changes: 10 additions & 0 deletions packages/playground/server/plugins/listener.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { defineNitroPlugin } from '#imports'

export default defineNitroPlugin((nitro) => {
nitro.hooks.hook('karbon:request', (ctx) => {
console.log('nitro request', ctx)
})
nitro.hooks.hook('karbon:response', (ctx) => {
console.log('nitro response', ctx)
})
})
Loading

0 comments on commit 7189b87

Please sign in to comment.