-
Notifications
You must be signed in to change notification settings - Fork 446
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore(core): add
getTransactionLogs
helper (#8261)
- Loading branch information
1 parent
cef5237
commit 03e7760
Showing
3 changed files
with
233 additions
and
23 deletions.
There are no files selected for viewing
118 changes: 118 additions & 0 deletions
118
packages/sanity/src/core/store/translog/getTransactionsLogs.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
import {type SanityClient} from '@sanity/client' | ||
import {beforeEach, describe, expect, it, type Mock, vi} from 'vitest' | ||
|
||
import {getJsonStream} from '../_legacy/history/history/getJsonStream' | ||
import {getTransactionsLogs} from './getTransactionsLogs' | ||
|
||
vi.mock('../_legacy/history/history/getJsonStream', () => ({ | ||
getJsonStream: vi.fn(), | ||
})) | ||
|
||
const getJsonStreamMock = getJsonStream as Mock | ||
|
||
describe('getTransactionsLogs', () => { | ||
const mockClient = { | ||
config: vi.fn(() => ({ | ||
dataset: 'mockDataset', | ||
token: 'mockToken', | ||
})), | ||
getUrl: vi.fn((path) => `https://mock.sanity.api${path}`), | ||
} as unknown as SanityClient | ||
|
||
const mockStream = { | ||
getReader: vi.fn(() => ({ | ||
async read() { | ||
return {done: true} | ||
}, | ||
})), | ||
} | ||
|
||
beforeEach(() => { | ||
vi.clearAllMocks() | ||
}) | ||
|
||
it('should fetch transaction logs with default parameters', async () => { | ||
getJsonStreamMock.mockResolvedValueOnce(mockStream) | ||
|
||
const documentId = 'doc1' | ||
const result = await getTransactionsLogs(mockClient, documentId, {}) | ||
|
||
expect(mockClient.getUrl).toHaveBeenCalledWith( | ||
'/data/history/mockDataset/transactions/doc1?tag=sanity.studio.transactions-log&excludeContent=true&limit=50&includeIdentifiedDocumentsOnly=true', | ||
) | ||
expect(getJsonStream).toHaveBeenCalledWith( | ||
'https://mock.sanity.api/data/history/mockDataset/transactions/doc1?tag=sanity.studio.transactions-log&excludeContent=true&limit=50&includeIdentifiedDocumentsOnly=true', | ||
'mockToken', | ||
) | ||
expect(result).toEqual([]) | ||
}) | ||
|
||
it('should handle multiple document IDs', async () => { | ||
getJsonStreamMock.mockResolvedValueOnce(mockStream) | ||
|
||
const documentIds = ['doc1', 'doc2'] | ||
await getTransactionsLogs(mockClient, documentIds, {}) | ||
|
||
expect(mockClient.getUrl).toHaveBeenCalledWith( | ||
'/data/history/mockDataset/transactions/doc1,doc2?tag=sanity.studio.transactions-log&excludeContent=true&limit=50&includeIdentifiedDocumentsOnly=true', | ||
) | ||
expect(getJsonStream).toHaveBeenCalledWith( | ||
'https://mock.sanity.api/data/history/mockDataset/transactions/doc1,doc2?tag=sanity.studio.transactions-log&excludeContent=true&limit=50&includeIdentifiedDocumentsOnly=true', | ||
'mockToken', | ||
) | ||
}) | ||
|
||
it('should override default parameters with user-provided params', async () => { | ||
getJsonStreamMock.mockResolvedValueOnce(mockStream) | ||
|
||
const documentId = 'doc1' | ||
await getTransactionsLogs(mockClient, documentId, { | ||
tag: 'sanity.studio.custom-tag', | ||
limit: 100, | ||
fromTransaction: 'tx1', | ||
toTransaction: 'tx3', | ||
}) | ||
|
||
expect(mockClient.getUrl).toHaveBeenCalledWith( | ||
'/data/history/mockDataset/transactions/doc1?tag=sanity.studio.custom-tag&excludeContent=true&limit=100&includeIdentifiedDocumentsOnly=true&fromTransaction=tx1&toTransaction=tx3', | ||
) | ||
}) | ||
|
||
it('should throw an error if the stream contains an error', async () => { | ||
const mockErrorStream = { | ||
getReader: vi.fn(() => ({ | ||
async read() { | ||
return {done: false, value: {error: {description: 'Error occurred'}}} | ||
}, | ||
})), | ||
} | ||
getJsonStreamMock.mockResolvedValueOnce(mockErrorStream) | ||
|
||
const documentId = 'doc1' | ||
|
||
await expect(getTransactionsLogs(mockClient, documentId, {})).rejects.toThrow('Error occurred') | ||
}) | ||
|
||
it('should collect transactions from the stream', async () => { | ||
const mockDataStream = { | ||
getReader: vi.fn(() => { | ||
let callCount = 0 | ||
return { | ||
async read() { | ||
if (callCount < 3) { | ||
callCount++ | ||
return {done: false, value: {id: `txn${callCount}`}} | ||
} | ||
return {done: true} | ||
}, | ||
} | ||
}), | ||
} | ||
getJsonStreamMock.mockResolvedValueOnce(mockDataStream) | ||
|
||
const documentId = 'doc1' | ||
const result = await getTransactionsLogs(mockClient as any, documentId, {}) | ||
|
||
expect(result).toEqual([{id: 'txn1'}, {id: 'txn2'}, {id: 'txn3'}]) | ||
}) | ||
}) |
108 changes: 108 additions & 0 deletions
108
packages/sanity/src/core/store/translog/getTransactionsLogs.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
import {type SanityClient} from '@sanity/client' | ||
import {type TransactionLogEventWithEffects} from '@sanity/types' | ||
|
||
import {getJsonStream} from '../_legacy/history/history/getJsonStream' | ||
|
||
/** | ||
* Fetches transaction logs for the specified document IDs from the translog | ||
* It adds the default query parameters to the request and also reads the stream of transactions. | ||
* @internal | ||
*/ | ||
export async function getTransactionsLogs( | ||
client: SanityClient, | ||
/** | ||
* 1 or more document IDs to fetch transaction logs */ | ||
documentIds: string | string[], | ||
/** | ||
* {@link https://www.sanity.io/docs/history-api#45ac5eece4ca} | ||
*/ | ||
params: { | ||
/** | ||
* The tag that will be use when fetching the transactions. | ||
* (Default: sanity.studio.transactions-log) | ||
*/ | ||
tag?: `sanity.studio.${string}` | ||
/** | ||
* Exclude the document contents from the responses. (You are required to set excludeContent as true for now.) | ||
* (Default: true) | ||
*/ | ||
excludeContent?: true | ||
/** | ||
* Limit the number of returned transactions. (Default: 50) | ||
*/ | ||
limit?: number | ||
|
||
/** | ||
* Only include the documents that are part of the document ids list | ||
* (Default: true) | ||
*/ | ||
includeIdentifiedDocumentsOnly?: boolean | ||
|
||
/** | ||
* How the effects are formatted in the response. | ||
* "mendoza": Super efficient format for expressing differences between JSON documents. {@link https://www.sanity.io/blog/mendoza} | ||
*/ | ||
effectFormat?: 'mendoza' | undefined | ||
/** | ||
* Return transactions in reverse order. | ||
*/ | ||
reverse?: boolean | ||
/** | ||
* Time from which the transactions are fetched. | ||
*/ | ||
fromTime?: string | ||
/** | ||
* Time until the transactions are fetched. | ||
*/ | ||
toTime?: string | ||
/** | ||
* Transaction ID (Or, Revision ID) from which the transactions are fetched. | ||
*/ | ||
fromTransaction?: string | ||
/** | ||
* Transaction ID (Or, Revision ID) until the transactions are fetched. | ||
*/ | ||
toTransaction?: string | ||
/** | ||
* Comma separated list of authors to filter the transactions by. | ||
*/ | ||
authors?: string | ||
}, | ||
): Promise<TransactionLogEventWithEffects[]> { | ||
const clientConfig = client.config() | ||
const dataset = clientConfig.dataset | ||
const queryParams = new URLSearchParams({ | ||
// Default values | ||
tag: 'sanity.studio.transactions-log', | ||
excludeContent: 'true', | ||
limit: '50', | ||
includeIdentifiedDocumentsOnly: 'true', | ||
}) | ||
Object.entries(params).forEach(([key, value]) => { | ||
if (value !== undefined) { | ||
queryParams.set(key, value.toString()) | ||
} | ||
}) | ||
|
||
const transactionsUrl = client.getUrl( | ||
`/data/history/${dataset}/transactions/${ | ||
Array.isArray(documentIds) ? documentIds.join(',') : documentIds | ||
}?${queryParams.toString()}`, | ||
) | ||
|
||
const stream = await getJsonStream(transactionsUrl, clientConfig.token) | ||
const transactions: TransactionLogEventWithEffects[] = [] | ||
|
||
const reader = stream.getReader() | ||
for (;;) { | ||
// eslint-disable-next-line no-await-in-loop | ||
const result = await reader.read() | ||
if (result.done) break | ||
|
||
if ('error' in result.value) { | ||
throw new Error(result.value.error.description || result.value.error.type) | ||
} | ||
transactions.push(result.value) | ||
} | ||
return transactions | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters