Skip to content

Commit

Permalink
fix(live): add withCredentials and tag support (#898)
Browse files Browse the repository at this point in the history
* fix(live): support `withCredentials: true`

* fix(live): add request tag support
  • Loading branch information
stipsan authored Oct 17, 2024
1 parent e29366e commit 4f882c9
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 13 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ jobs:
name: build-output
- uses: denoland/setup-deno@v1
with:
deno-version: vx.x.x
deno-version: v1.x.x
- run: npm run test:deno

bun-runtime:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/deno.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
node-version: lts/*
- uses: denoland/setup-deno@v1
with:
deno-version: vx.x.x
deno-version: v1.x.x
- run: npm run test:deno:update_import_map
- uses: actions/create-github-app-token@v1
id: app-token
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@sanity/client",
"version": "6.22.1",
"version": "6.22.2-canary.1",
"description": "Client for retrieving, creating and patching data from Sanity.io",
"keywords": [
"sanity",
Expand Down
39 changes: 32 additions & 7 deletions src/data/live.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import {Observable} from 'rxjs'

import type {ObservableSanityClient, SanityClient} from '../SanityClient'
import type {Any, LiveEventMessage, LiveEventRestart} from '../types'
import type {
Any,
LiveEventMessage,
LiveEventReconnect,
LiveEventRestart,
LiveEventWelcome,
} from '../types'
import {_getDataUrl} from './dataMethods'

const requiredApiVersion = '2021-03-26'
Expand All @@ -20,11 +26,23 @@ export class LiveClient {
*/
events({
includeDrafts = false,
tag: _tag,
}: {
/** @alpha this API is experimental and may change or even be removed */
includeDrafts?: boolean
} = {}): Observable<LiveEventMessage | LiveEventRestart> {
const {apiVersion: _apiVersion, token} = this.#client.config()
/**
* Optional request tag for the listener. Use to identify the request in logs.
*
* @defaultValue `undefined`
*/
tag?: string
} = {}): Observable<LiveEventMessage | LiveEventRestart | LiveEventReconnect | LiveEventWelcome> {
const {
apiVersion: _apiVersion,
token,
withCredentials,
requestTagPrefix,
} = this.#client.config()
const apiVersion = _apiVersion.replace(/^v/, '')
if (apiVersion !== 'X' && apiVersion < requiredApiVersion) {
throw new Error(
Expand All @@ -33,9 +51,9 @@ export class LiveClient {
`Please update your API version to use this feature.`,
)
}
if (includeDrafts && !token) {
if (includeDrafts && !token && !withCredentials) {
throw new Error(
`The live events API requires a token when 'includeDrafts: true'. Please update your client configuration. The token should have the lowest possible access role.`,
`The live events API requires a token or withCredentials when 'includeDrafts: true'. Please update your client configuration. The token should have the lowest possible access role.`,
)
}
if (includeDrafts && apiVersion !== 'X') {
Expand All @@ -45,17 +63,24 @@ export class LiveClient {
}
const path = _getDataUrl(this.#client, 'live/events')
const url = new URL(this.#client.getUrl(path, false))
const tag = _tag && requestTagPrefix ? [requestTagPrefix, _tag].join('.') : _tag
if (tag) {
url.searchParams.set('tag', tag)
}
if (includeDrafts) {
url.searchParams.set('includeDrafts', 'true')
}

const listenFor = ['restart', 'message'] as const
const listenFor = ['restart', 'message', 'welcome', 'reconnect'] as const
const esOptions: EventSourceInit & {headers?: Record<string, string>} = {}
if (includeDrafts && token) {
esOptions.headers = {
Authorization: `Bearer ${token}`,
}
}
if (includeDrafts && withCredentials) {
esOptions.withCredentials = true
}

return new Observable((observer) => {
let es: InstanceType<typeof EventSource> | undefined
Expand Down Expand Up @@ -108,7 +133,7 @@ export class LiveClient {

async function getEventSource() {
const EventSourceImplementation: typeof EventSource =
typeof EventSource === 'undefined' || esOptions.headers
typeof EventSource === 'undefined' || esOptions.headers || esOptions.withCredentials
? ((await import('@sanity/eventsource')).default as unknown as typeof EventSource)
: EventSource

Expand Down
9 changes: 9 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1244,13 +1244,22 @@ export type SyncTag = `s1:${string}`
/** @public */
export interface LiveEventRestart {
type: 'restart'
id: string
}
/** @public */
export interface LiveEventReconnect {
type: 'reconnect'
}
/** @public */
export interface LiveEventMessage {
type: 'message'
id: string
tags: SyncTag[]
}
/** @public */
export interface LiveEventWelcome {
type: 'welcome'
}

/** @public */
export interface SanityQueries {}
Expand Down
3 changes: 2 additions & 1 deletion test/live.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ describe.skipIf(typeof EdgeRuntime === 'string' || typeof document !== 'undefine
test('requires token when includeDrafts is true', () => {
const client = getClient({apiVersion: 'vX', port: 1234})
expect(() => client.live.events({includeDrafts: true})).toThrowErrorMatchingInlineSnapshot(
`[Error: The live events API requires a token when 'includeDrafts: true'. Please update your client configuration. The token should have the lowest possible access role.]`,
`[Error: The live events API requires a token or withCredentials when 'includeDrafts: true'. Please update your client configuration. The token should have the lowest possible access role.]`,
)
})
test('requires apiVersion X when includeDrafts is true', () => {
Expand Down Expand Up @@ -171,6 +171,7 @@ describe.skipIf(typeof EdgeRuntime === 'string' || typeof document !== 'undefine
await new Promise<void>((resolve, reject) => {
const subscription = client.live.events().subscribe({
next: (msg) => {
if (msg.type === 'welcome') return
expect(msg.type, 'emits restart events to tell the client to reset local state').toBe(
'restart',
)
Expand Down

0 comments on commit 4f882c9

Please sign in to comment.