diff --git a/packages/sanity/src/core/store/_legacy/document/document-pair/actionTypes.ts b/packages/sanity/src/core/store/_legacy/document/document-pair/actionTypes.ts deleted file mode 100644 index 89e4c1b663e..00000000000 --- a/packages/sanity/src/core/store/_legacy/document/document-pair/actionTypes.ts +++ /dev/null @@ -1,26 +0,0 @@ -import {type PatchMutation} from '@sanity/types' - -export interface HttpCreateAction { - actionType: 'sanity.action.document.create' - publishedId: string - attributes: { - _id: string - _type: string - } - ifExists: 'ignore' | 'fail' -} - -export interface HttpDeleteAction { - actionType: 'sanity.action.document.delete' - draftId: string - publishedId: string -} - -export interface HttpEditAction { - actionType: 'sanity.action.document.edit' - draftId: string - publishedId: string - patch: Omit -} - -export type HttpAction = HttpCreateAction | HttpDeleteAction | HttpEditAction diff --git a/packages/sanity/src/core/store/_legacy/document/document-pair/checkoutPair.test.ts b/packages/sanity/src/core/store/_legacy/document/document-pair/checkoutPair.test.ts index 305d5c19d35..0abb38c51e2 100644 --- a/packages/sanity/src/core/store/_legacy/document/document-pair/checkoutPair.test.ts +++ b/packages/sanity/src/core/store/_legacy/document/document-pair/checkoutPair.test.ts @@ -6,7 +6,7 @@ import {delay} from 'rxjs/operators' import {checkoutPair} from './checkoutPair' const mockedDataRequest = jest.fn(() => of({})) -const mockedObservableRequest = jest.fn(() => of({})) +const mockedActionRequest = jest.fn(() => of({})) const client = { observable: { @@ -16,19 +16,11 @@ const client = { {_id: ids[0], _type: 'any', _rev: 'any'}, {_id: ids[1], _type: 'any', _rev: 'any'}, ]), + action: mockedActionRequest, }, dataRequest: mockedDataRequest, } -const clientWithConfig = { - ...client, - withConfig: () => ({ - ...client, - observable: {...client.observable, request: mockedObservableRequest}, - }), - config: () => ({dataset: 'production'}), -} - const idPair = {publishedId: 'publishedId', draftId: 'draftId'} beforeEach(() => { @@ -223,11 +215,7 @@ describe('checkoutPair -- local actions', () => { describe('checkoutPair -- server actions', () => { test('patch', async () => { - const {draft, published} = checkoutPair( - clientWithConfig as any as SanityClient, - idPair, - of(true), - ) + const {draft, published} = checkoutPair(client as any as SanityClient, idPair, of(true)) const combined = merge(draft.events, published.events) const sub = combined.subscribe() await new Promise((resolve) => setTimeout(resolve, 0)) @@ -235,36 +223,30 @@ describe('checkoutPair -- server actions', () => { draft.mutate(draft.patch([{set: {title: 'new title'}}])) draft.commit() - expect(mockedObservableRequest).toHaveBeenCalledWith({ - url: '/data/actions/production', - method: 'post', - tag: 'document.commit', - body: { - transactionId: expect.any(String), - actions: [ - { - actionType: 'sanity.action.document.edit', - draftId: 'draftId', - publishedId: 'publishedId', - patch: { - set: { - title: 'new title', - }, + expect(mockedActionRequest).toHaveBeenCalledWith( + [ + { + actionType: 'sanity.action.document.edit', + draftId: 'draftId', + publishedId: 'publishedId', + patch: { + set: { + title: 'new title', }, }, - ], + }, + ], + { + tag: 'document.commit', + transactionId: expect.any(String), }, - }) + ) sub.unsubscribe() }) test('published patch uses mutation endpoint', async () => { - const {draft, published} = checkoutPair( - clientWithConfig as any as SanityClient, - idPair, - of(true), - ) + const {draft, published} = checkoutPair(client as any as SanityClient, idPair, of(true)) const combined = merge(draft.events, published.events) const sub = combined.subscribe() await new Promise((resolve) => setTimeout(resolve, 0)) @@ -273,7 +255,7 @@ describe('checkoutPair -- server actions', () => { published.mutate(published.patch([{set: {title: 'new title'}}])) published.commit() - expect(mockedObservableRequest).not.toHaveBeenCalled() + expect(mockedActionRequest).not.toHaveBeenCalled() expect(mockedDataRequest).toHaveBeenCalledWith( 'mutate', @@ -293,11 +275,7 @@ describe('checkoutPair -- server actions', () => { }) test('create', async () => { - const {draft, published} = checkoutPair( - clientWithConfig as any as SanityClient, - idPair, - of(true), - ) + const {draft, published} = checkoutPair(client as any as SanityClient, idPair, of(true)) const combined = merge(draft.events, published.events) const sub = combined.subscribe() await new Promise((resolve) => setTimeout(resolve, 0)) @@ -313,36 +291,30 @@ describe('checkoutPair -- server actions', () => { ]) draft.commit() - expect(mockedObservableRequest).toHaveBeenCalledWith({ - url: '/data/actions/production', - method: 'post', - tag: 'document.commit', - body: { - transactionId: expect.any(String), - actions: [ - { - actionType: 'sanity.action.document.create', - publishedId: 'publishedId', - attributes: { - _id: 'draftId', - _type: 'any', - _createdAt: 'now', - }, - ifExists: 'fail', + expect(mockedActionRequest).toHaveBeenCalledWith( + [ + { + actionType: 'sanity.action.document.create', + publishedId: 'publishedId', + attributes: { + _id: 'draftId', + _type: 'any', + _createdAt: 'now', }, - ], + ifExists: 'fail', + }, + ], + { + tag: 'document.commit', + transactionId: expect.any(String), }, - }) + ) sub.unsubscribe() }) test('createIfNotExists', async () => { - const {draft, published} = checkoutPair( - clientWithConfig as any as SanityClient, - idPair, - of(true), - ) + const {draft, published} = checkoutPair(client as any as SanityClient, idPair, of(true)) const combined = merge(draft.events, published.events) const sub = combined.subscribe() await new Promise((resolve) => setTimeout(resolve, 0)) @@ -358,14 +330,9 @@ describe('checkoutPair -- server actions', () => { ]) draft.commit() - expect(mockedObservableRequest).toHaveBeenCalledWith({ - url: '/data/actions/production', - method: 'post', + expect(mockedActionRequest).toHaveBeenCalledWith([], { tag: 'document.commit', - body: { - transactionId: expect.any(String), - actions: [], - }, + transactionId: expect.any(String), }) sub.unsubscribe() diff --git a/packages/sanity/src/core/store/_legacy/document/document-pair/checkoutPair.ts b/packages/sanity/src/core/store/_legacy/document/document-pair/checkoutPair.ts index 1130afe454b..0bf28b3271c 100644 --- a/packages/sanity/src/core/store/_legacy/document/document-pair/checkoutPair.ts +++ b/packages/sanity/src/core/store/_legacy/document/document-pair/checkoutPair.ts @@ -1,4 +1,4 @@ -import {type SanityClient} from '@sanity/client' +import {type Action, type SanityClient} from '@sanity/client' import {type Mutation} from '@sanity/mutator' import {type SanityDocument} from '@sanity/types' import {omit} from 'lodash' @@ -14,7 +14,6 @@ import { } from '../buffered-doc' import {getPairListener, type ListenerEvent} from '../getPairListener' import {type IdPair, type PendingMutationsEvent, type ReconnectEvent} from '../types' -import {type HttpAction} from './actionTypes' const isMutationEventForDocId = (id: string) => @@ -95,12 +94,8 @@ function isLiveEditMutation(mutationParams: Mutation['params'], publishedId: str return patchTargets.every((target) => target === publishedId) } -function toActions(idPair: IdPair, mutationParams: Mutation['params']) { - return mutationParams.mutations.flatMap((mutations) => { - if (Object.keys(mutations).length > 1) { - // todo: this might be a bit too strict, but I'm (lazily) trying to check if we ever get more than one mutation in a payload - throw new Error('Did not expect multiple mutations in the same payload') - } +function toActions(idPair: IdPair, mutationParams: Mutation['params']): Action[] { + return mutationParams.mutations.flatMap((mutations) => { // This action is not always interoperable with the equivalent mutation. It will fail if the // published version of the document already exists. if (mutations.createIfNotExists) { @@ -125,30 +120,18 @@ function toActions(idPair: IdPair, mutationParams: Mutation['params']) { patch: omit(mutations.patch, 'id'), } } - throw new Error('Todo: implement') + throw new Error('Cannot map mutation to action') }) } -function commitActions( - defaultClient: SanityClient, - idPair: IdPair, - mutationParams: Mutation['params'], -) { +function commitActions(client: SanityClient, idPair: IdPair, mutationParams: Mutation['params']) { if (isLiveEditMutation(mutationParams, idPair.publishedId)) { - return commitMutations(defaultClient, mutationParams) + return commitMutations(client, mutationParams) } - const vXClient = defaultClient.withConfig({apiVersion: 'X'}) - const {dataset} = defaultClient.config() - - return vXClient.observable.request({ - url: `/data/actions/${dataset}`, - method: 'post', + return client.observable.action(toActions(idPair, mutationParams), { tag: 'document.commit', - body: { - transactionId: mutationParams.transactionId, - actions: toActions(idPair, mutationParams), - }, + transactionId: mutationParams.transactionId, }) } diff --git a/packages/sanity/src/core/store/_legacy/document/document-pair/operations/__snapshots__/publish.test.ts.snap b/packages/sanity/src/core/store/_legacy/document/document-pair/operations/__snapshots__/publish.test.ts.snap index 7cfa160fd93..ac365b6da72 100644 --- a/packages/sanity/src/core/store/_legacy/document/document-pair/operations/__snapshots__/publish.test.ts.snap +++ b/packages/sanity/src/core/store/_legacy/document/document-pair/operations/__snapshots__/publish.test.ts.snap @@ -4,6 +4,7 @@ exports[`publish execute calls createOrReplace with _revision_lock_pseudo_field_ Object { "listen": Array [], "observable": Object { + "action": Array [], "fetch": Array [], "getDocuments": Array [], "listen": Array [], @@ -59,6 +60,7 @@ exports[`publish execute removes the \`_updatedAt\` field 1`] = ` Object { "listen": Array [], "observable": Object { + "action": Array [], "fetch": Array [], "getDocuments": Array [], "listen": Array [], @@ -104,6 +106,7 @@ exports[`publish execute takes in any and strengthens references where _strength Object { "listen": Array [], "observable": Object { + "action": Array [], "fetch": Array [], "getDocuments": Array [], "listen": Array [], diff --git a/packages/sanity/src/core/store/_legacy/document/document-pair/serverOperations/__snapshots__/publish.test.ts.snap b/packages/sanity/src/core/store/_legacy/document/document-pair/serverOperations/__snapshots__/publish.test.ts.snap index 39a2a649480..7e0895c73d4 100644 --- a/packages/sanity/src/core/store/_legacy/document/document-pair/serverOperations/__snapshots__/publish.test.ts.snap +++ b/packages/sanity/src/core/store/_legacy/document/document-pair/serverOperations/__snapshots__/publish.test.ts.snap @@ -4,27 +4,24 @@ exports[`publish execute calls createOrReplace with _revision_lock_pseudo_field_ Object { "listen": Array [], "observable": Object { - "fetch": Array [], - "getDocuments": Array [], - "listen": Array [], - "request": Array [ + "action": Array [ Object { - "body": Object { - "actions": Array [ - Object { - "actionType": "sanity.action.document.publish", - "draftId": "drafts.my-id", - "ifDraftRevisionId": "exampleRev", - "ifPublishedRevisionId": "exampleRev", - "publishedId": "my-id", - }, - ], + "actions": Object { + "actionType": "sanity.action.document.publish", + "draftId": "drafts.my-id", + "ifDraftRevisionId": "exampleRev", + "ifPublishedRevisionId": "exampleRev", + "publishedId": "my-id", + }, + "options": Object { + "tag": "document.publish", }, - "method": "post", - "tag": "document.publish", - "url": "/data/actions/mock-data-set", }, ], + "fetch": Array [], + "getDocuments": Array [], + "listen": Array [], + "request": Array [], }, "request": Array [], "transaction": Array [], @@ -35,27 +32,24 @@ exports[`publish execute removes the \`_updatedAt\` field 1`] = ` Object { "listen": Array [], "observable": Object { - "fetch": Array [], - "getDocuments": Array [], - "listen": Array [], - "request": Array [ + "action": Array [ Object { - "body": Object { - "actions": Array [ - Object { - "actionType": "sanity.action.document.publish", - "draftId": "drafts.my-id", - "ifDraftRevisionId": "exampleRev", - "ifPublishedRevisionId": undefined, - "publishedId": "my-id", - }, - ], + "actions": Object { + "actionType": "sanity.action.document.publish", + "draftId": "drafts.my-id", + "ifDraftRevisionId": "exampleRev", + "ifPublishedRevisionId": undefined, + "publishedId": "my-id", + }, + "options": Object { + "tag": "document.publish", }, - "method": "post", - "tag": "document.publish", - "url": "/data/actions/mock-data-set", }, ], + "fetch": Array [], + "getDocuments": Array [], + "listen": Array [], + "request": Array [], }, "request": Array [], "transaction": Array [], @@ -66,27 +60,24 @@ exports[`publish execute takes in any and strengthens references where _strength Object { "listen": Array [], "observable": Object { - "fetch": Array [], - "getDocuments": Array [], - "listen": Array [], - "request": Array [ + "action": Array [ Object { - "body": Object { - "actions": Array [ - Object { - "actionType": "sanity.action.document.publish", - "draftId": "drafts.my-id", - "ifDraftRevisionId": "exampleRev", - "ifPublishedRevisionId": undefined, - "publishedId": "my-id", - }, - ], + "actions": Object { + "actionType": "sanity.action.document.publish", + "draftId": "drafts.my-id", + "ifDraftRevisionId": "exampleRev", + "ifPublishedRevisionId": undefined, + "publishedId": "my-id", + }, + "options": Object { + "tag": "document.publish", }, - "method": "post", - "tag": "document.publish", - "url": "/data/actions/mock-data-set", }, ], + "fetch": Array [], + "getDocuments": Array [], + "listen": Array [], + "request": Array [], }, "request": Array [], "transaction": Array [], @@ -97,6 +88,7 @@ exports[`publish execute throws an error if the client has no draft snaphot 1`] Object { "listen": Array [], "observable": Object { + "action": Array [], "fetch": Array [], "getDocuments": Array [], "listen": Array [], diff --git a/packages/sanity/src/core/store/_legacy/document/document-pair/serverOperations/delete.ts b/packages/sanity/src/core/store/_legacy/document/document-pair/serverOperations/delete.ts index d6ba20e6f8a..efcac4ba851 100644 --- a/packages/sanity/src/core/store/_legacy/document/document-pair/serverOperations/delete.ts +++ b/packages/sanity/src/core/store/_legacy/document/document-pair/serverOperations/delete.ts @@ -3,50 +3,38 @@ import {isLiveEditEnabled} from '../utils/isLiveEditEnabled' export const del: OperationImpl<[], 'NOTHING_TO_DELETE'> = { disabled: ({snapshots}) => (snapshots.draft || snapshots.published ? false : 'NOTHING_TO_DELETE'), - execute: ({client: globalClient, schema, idPair, typeName, snapshots}) => { + execute: ({client, schema, idPair, typeName, snapshots}) => { if (isLiveEditEnabled(schema, typeName)) { - const tx = globalClient.observable.transaction().delete(idPair.publishedId) + const tx = client.observable.transaction().delete(idPair.publishedId) return tx.commit({tag: 'document.delete'}) } - const vXClient = globalClient.withConfig({apiVersion: 'X'}) - const {dataset} = globalClient.config() - //the delete action requires a published doc -- discard if not present if (!snapshots.published) { - return vXClient.observable.request({ - url: `/data/actions/${dataset}`, - method: 'post', - tag: 'document.discard', - body: { - actions: [ - { - actionType: 'sanity.action.document.discard', - draftId: idPair.draftId, - publishedId: idPair.publishedId, - }, - ], + return client.observable.action( + { + actionType: 'sanity.action.document.discard', + draftId: idPair.draftId, + purge: false, }, - }) + {tag: 'document.delete'}, + ) } - return vXClient.observable.request({ - url: `/data/actions/${dataset}`, - method: 'post', - tag: 'document.delete', - // this disables referential integrity for cross-dataset references. we - // have this set because we warn against deletes in the `ConfirmDeleteDialog` - // UI. This operation is run when "delete anyway" is clicked - query: {skipCrossDatasetReferenceValidation: 'true'}, - body: { - actions: [ - { - actionType: 'sanity.action.document.delete', - draftId: idPair.draftId, - publishedId: idPair.publishedId, - }, - ], + return client.observable.action( + { + actionType: 'sanity.action.document.delete', + includeDrafts: snapshots.draft ? [idPair.draftId] : [], + publishedId: idPair.publishedId, + purge: false, + }, + { + tag: 'document.delete', + // this disables referential integrity for cross-dataset references. we + // have this set because we warn against deletes in the `ConfirmDeleteDialog` + // UI. This operation is run when "delete anyway" is clicked + skipCrossDatasetReferenceValidation: true, }, - }) + ) }, } diff --git a/packages/sanity/src/core/store/_legacy/document/document-pair/serverOperations/discardChanges.ts b/packages/sanity/src/core/store/_legacy/document/document-pair/serverOperations/discardChanges.ts index 7dfa865d173..d0454de1b8e 100644 --- a/packages/sanity/src/core/store/_legacy/document/document-pair/serverOperations/discardChanges.ts +++ b/packages/sanity/src/core/store/_legacy/document/document-pair/serverOperations/discardChanges.ts @@ -12,23 +12,14 @@ export const discardChanges: OperationImpl<[], DisabledReason> = { } return false }, - execute: ({client: globalClient, idPair}) => { - const vXClient = globalClient.withConfig({apiVersion: 'X'}) - const {dataset} = globalClient.config() - - return vXClient.observable.request({ - url: `/data/actions/${dataset}`, - method: 'post', - tag: 'document.discard-changes', - body: { - actions: [ - { - actionType: 'sanity.action.document.discard', - draftId: idPair.draftId, - publishedId: idPair.publishedId, - }, - ], + execute: ({client, idPair}) => { + return client.observable.action( + { + actionType: 'sanity.action.document.discard', + draftId: idPair.draftId, + purge: false, }, - }) + {tag: 'document.discard-changes'}, + ) }, } diff --git a/packages/sanity/src/core/store/_legacy/document/document-pair/serverOperations/publish.ts b/packages/sanity/src/core/store/_legacy/document/document-pair/serverOperations/publish.ts index 35ed99080b8..cdc721ab194 100644 --- a/packages/sanity/src/core/store/_legacy/document/document-pair/serverOperations/publish.ts +++ b/packages/sanity/src/core/store/_legacy/document/document-pair/serverOperations/publish.ts @@ -13,34 +13,28 @@ export const publish: OperationImpl<[], DisabledReason> = { } return false }, - execute: ({client: globalClient, idPair, snapshots}) => { - const vXClient = globalClient.withConfig({apiVersion: 'X'}) - const {dataset} = globalClient.config() - + execute: ({client, idPair, snapshots}) => { // The editor must be able to see the draft they are choosing to publish. if (!snapshots.draft) { throw new Error('cannot execute "publish" when draft is missing') } - return vXClient.observable.request({ - url: `/data/actions/${dataset}`, - method: 'post', - tag: 'document.publish', - body: { - actions: [ - { - actionType: 'sanity.action.document.publish', - draftId: idPair.draftId, - publishedId: idPair.publishedId, - // The editor must be able to see the latest state of both the draft document they are - // publishing, and the published document they are choosing to replace. Optimistic - // locking using `ifDraftRevisionId` and `ifPublishedRevisionId` ensures the client and - // server are synchronised. - ifDraftRevisionId: snapshots.draft._rev, - ifPublishedRevisionId: snapshots.published?._rev, - }, - ], + return client.observable.action( + { + actionType: 'sanity.action.document.publish', + draftId: idPair.draftId, + publishedId: idPair.publishedId, + // The editor must be able to see the latest state of both the draft document they are + // publishing, and the published document they are choosing to replace. Optimistic + // locking using `ifDraftRevisionId` and `ifPublishedRevisionId` ensures the client and + // server are synchronised. + ifDraftRevisionId: snapshots.draft._rev, + // @ts-expect-error FIXME + ifPublishedRevisionId: snapshots.published?._rev, + }, + { + tag: 'document.publish', }, - }) + ) }, } diff --git a/packages/sanity/src/core/store/_legacy/document/document-pair/serverOperations/unpublish.ts b/packages/sanity/src/core/store/_legacy/document/document-pair/serverOperations/unpublish.ts index b88866f7769..10852fb1058 100644 --- a/packages/sanity/src/core/store/_legacy/document/document-pair/serverOperations/unpublish.ts +++ b/packages/sanity/src/core/store/_legacy/document/document-pair/serverOperations/unpublish.ts @@ -10,27 +10,20 @@ export const unpublish: OperationImpl<[], DisabledReason> = { } return snapshots.published ? false : 'NOT_PUBLISHED' }, - execute: ({client: globalClient, idPair}) => { - const vXClient = globalClient.withConfig({apiVersion: 'X'}) - const {dataset} = globalClient.config() - - return vXClient.observable.request({ - url: `/data/actions/${dataset}`, - method: 'post', - // this disables referential integrity for cross-dataset references. we - // have this set because we warn against unpublishes in the `ConfirmDeleteDialog` - // UI. This operation is run when "unpublish anyway" is clicked - query: {skipCrossDatasetReferenceValidation: 'true'}, - tag: 'document.unpublish', - body: { - actions: [ - { - actionType: 'sanity.action.document.unpublish', - draftId: idPair.draftId, - publishedId: idPair.publishedId, - }, - ], + execute: ({client, idPair}) => + client.observable.action( + { + // This operation is run when "unpublish anyway" is clicked + actionType: 'sanity.action.document.unpublish', + draftId: idPair.draftId, + publishedId: idPair.publishedId, }, - }) - }, + { + tag: 'document.unpublish', + // this disables referential integrity for cross-dataset references. we + // have this set because we warn against unpublishes in the `ConfirmDeleteDialog` + // UI. + skipCrossDatasetReferenceValidation: true, + }, + ), } diff --git a/packages/sanity/src/core/store/_legacy/document/document-pair/utils/fetchFeatureToggle.ts b/packages/sanity/src/core/store/_legacy/document/document-pair/utils/fetchFeatureToggle.ts index 448bdaba4f4..81d3090b567 100644 --- a/packages/sanity/src/core/store/_legacy/document/document-pair/utils/fetchFeatureToggle.ts +++ b/packages/sanity/src/core/store/_legacy/document/document-pair/utils/fetchFeatureToggle.ts @@ -1,36 +1,51 @@ import {type SanityClient} from '@sanity/client' import {map, type Observable, of, ReplaySubject, timeout, timer} from 'rxjs' -import {catchError, share} from 'rxjs/operators' +import {catchError, concatMap, share} from 'rxjs/operators' interface ActionsFeatureToggle { actions: boolean } +const CACHE = new WeakMap>() -//in the "real" code, this would be observable.request, to a URI -export const fetchFeatureToggle = (defaultClient: SanityClient): Observable => { - const client = defaultClient.withConfig({apiVersion: 'X'}) - const {dataset} = defaultClient.config() +// How often to refresh the feature toggle +const REFRESH_INTERVAL = 1000 * 120 - return client.observable - .request({ - uri: `/data/actions/${dataset}`, - withCredentials: true, - }) - .pipe( - map((res: ActionsFeatureToggle) => res.actions), - timeout({first: 2000, with: () => of(false)}), - catchError(() => - // If we fail to fetch the feature toggle, we'll just assume it's disabled and fallback to legacy mutations - of(false), - ), - share({ - // replay latest known state to new subscribers - connector: () => new ReplaySubject(1), - // this will typically be completed and unsubscribed from right after the answer is received, so we don't want to reset - resetOnRefCountZero: false, - // once the fetch has completed, we'll wait for 2 minutes before resetting the state. - // we'll then check again once a new subscriber comes in - resetOnComplete: () => timer(1000 * 120), - }), - ) +// Timer used to reset the observable when it completes or it's refcount drops to zero +const RESET_TIMER = timer(REFRESH_INTERVAL) + +function createFeatureToggle(client: SanityClient) { + const {dataset} = client.config() + + return timer(0, REFRESH_INTERVAL).pipe( + concatMap(() => + client.observable + .request({ + uri: `/data/actions/${dataset}`, + withCredentials: true, + }) + .pipe( + map((res: ActionsFeatureToggle) => res.actions), + timeout({first: 2000, with: () => of(false)}), + catchError(() => + // If we fail to fetch the feature toggle, we'll just assume it's disabled and fallback to legacy mutations + of(false), + ), + ), + ), + share({ + // replay latest known state to new subscribers + connector: () => new ReplaySubject(1), + // this will typically be completed and unsubscribed from right after the answer is received, so we don't want to reset + resetOnComplete: () => RESET_TIMER, + // keep it alive for some time after the last subscriber unsubscribes + resetOnRefCountZero: () => RESET_TIMER, + }), + ) +} + +export const fetchFeatureToggle = (client: SanityClient): Observable => { + if (!CACHE.has(client)) { + CACHE.set(client, createFeatureToggle(client)) + } + return CACHE.get(client)! } diff --git a/packages/sanity/src/core/store/_legacy/history/createHistoryStore.ts b/packages/sanity/src/core/store/_legacy/history/createHistoryStore.ts index 358fd1444c7..b8798e0e05d 100644 --- a/packages/sanity/src/core/store/_legacy/history/createHistoryStore.ts +++ b/packages/sanity/src/core/store/_legacy/history/createHistoryStore.ts @@ -29,12 +29,7 @@ export interface HistoryStore { getTransactions: (documentIds: string[]) => Promise - restore: ( - id: string, - targetId: string, - rev: string, - options?: RestoreOptions, - ) => Observable + restore: (id: string, targetId: string, rev: string, options?: RestoreOptions) => Observable /** @internal */ getTimelineController: (options: { @@ -192,10 +187,7 @@ function restore( targetDocumentId: string, rev: string, options?: RestoreOptions, -) { - const vXClient = client.withConfig({apiVersion: 'X'}) - const {dataset} = client.config() - +): Observable { return from(getDocumentAtRevision(client, documentId, rev)).pipe( mergeMap((documentAtRevision) => { if (!documentAtRevision) { @@ -217,23 +209,25 @@ function restore( }), mergeMap((restoredDraft) => { if (options?.useServerDocumentActions) { - return vXClient.observable.request({ - url: `/data/actions/${dataset}`, - method: 'post', - body: { - actions: [ - { - actionType: 'sanity.action.document.replaceDraft', - publishedId: documentId, - attributes: restoredDraft, - }, - ], + return client.observable.action([ + { + actionType: 'sanity.action.document.create', + publishedId: documentId, + attributes: restoredDraft, + ifExists: 'ignore', + }, + // @ts-expect-error FIXME + { + actionType: 'sanity.action.document.replaceDraft', + publishedId: documentId, + attributes: restoredDraft, }, - }) + ]) } return client.observable.createOrReplace(restoredDraft, {visibility: 'async'}) }), + map(() => undefined), ) } diff --git a/packages/sanity/src/core/studioClient.ts b/packages/sanity/src/core/studioClient.ts index 2982e707d36..7e8dc9e9284 100644 --- a/packages/sanity/src/core/studioClient.ts +++ b/packages/sanity/src/core/studioClient.ts @@ -10,5 +10,5 @@ import {type SourceClientOptions} from './config' * @internal */ export const DEFAULT_STUDIO_CLIENT_OPTIONS: SourceClientOptions = { - apiVersion: '2024-03-12', + apiVersion: '2024-05-28', } diff --git a/packages/sanity/test/mocks/mockSanityClient.ts b/packages/sanity/test/mocks/mockSanityClient.ts index 01fb3705171..8154117d2d9 100644 --- a/packages/sanity/test/mocks/mockSanityClient.ts +++ b/packages/sanity/test/mocks/mockSanityClient.ts @@ -19,6 +19,7 @@ export interface MockClientLog { getDocuments: {ids: string[]}[] listen: {query: string; params?: any}[] request: any[] + action: {actions: any[]; options: {transctionId: string; tag: string}}[] } request: any[] transaction: MockClientTransactionLog[] @@ -85,6 +86,7 @@ export function createMockSanityClient( getDocuments: [], listen: [], request: [], + action: [], }, request: [], transaction: [], @@ -122,6 +124,10 @@ export function createMockSanityClient( }, observable: { + action: (actions: any[], opts: {transctionId: string; tag: string}) => { + $log.observable.action.push({actions, options: opts}) + return of({}) + }, fetch: (query: string, params?: any): Observable => { $log.observable.fetch.push({query, params}) // $log('observable.fetch', {query, params})