Skip to content

Commit

Permalink
fix(actions): add unit tests, move actionType out of types
Browse files Browse the repository at this point in the history
  • Loading branch information
j33ty committed May 23, 2024
1 parent 38faba4 commit 5c8e6f2
Show file tree
Hide file tree
Showing 3 changed files with 205 additions and 24 deletions.
28 changes: 25 additions & 3 deletions src/data/dataMethods.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,12 +255,33 @@ export function _action(
const transactionId = (options && options.transactionId) || undefined
const skipCrossDatasetReferenceValidation =
(options && options.skipCrossDatasetReferenceValidation) || undefined
const dryRun = (options && options.dryRun) || undefined

const actionWithTypes = acts.map((act: Action) => {
if ('create' in act) {
return {actionType: 'sanity.action.document.create', ...act.create}
} else if ('replaceDraft' in act) {
return {actionType: 'sanity.action.document.replaceDraft', ...act.replaceDraft}
} else if ('edit' in act) {
return {actionType: 'sanity.action.document.edit', ...act.edit}
} else if ('delete' in act) {
return {actionType: 'sanity.action.document.delete', ...act.delete}
} else if ('discard' in act) {
return {actionType: 'sanity.action.document.discard', ...act.discard}
} else if ('publish' in act) {
return {actionType: 'sanity.action.document.publish', ...act.publish}
} else if ('unpublish' in act) {
return {actionType: 'sanity.action.document.unpublish', ...act.unpublish}
}

return act
})

return _dataRequest(
client,
httpRequest,
'actions',
{actions: acts, transactionId, skipCrossDatasetReferenceValidation},
{actions: actionWithTypes, transactionId, skipCrossDatasetReferenceValidation, dryRun},
options,
)
}
Expand All @@ -276,12 +297,13 @@ export function _dataRequest(
options: Any = {},
): Any {
const isMutation = endpoint === 'mutate'
const isAction = endpoint === 'actions'
const isQuery = endpoint === 'query'

// Check if the query string is within a configured threshold,
// in which case we can use GET. Otherwise, use POST.
const strQuery = isMutation ? '' : encodeQueryString(body)
const useGet = !isMutation && strQuery.length < getQuerySizeLimit
const strQuery = isMutation || isAction ? '' : encodeQueryString(body)
const useGet = !isMutation && !isAction && strQuery.length < getQuerySizeLimit
const stringQuery = useGet ? strQuery : ''
const returnFirst = options.returnFirst
const {timeout, token, tag, headers, returnQuery, lastLiveEventId} = options
Expand Down
42 changes: 21 additions & 21 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -513,22 +513,22 @@ export type Mutation<R extends Record<string, Any> = Record<string, Any>> =

/** @public */
export type Action =
| CreateAction
| ReplaceDraftAction
| EditAction
| DeleteAction
| DiscardAction
| PublishAction
| UnpublishAction
| {create: CreateAction}
| {replaceDraft: ReplaceDraftAction}
| {edit: EditAction}
| {delete: DeleteAction}
| {discard: DiscardAction}
| {publish: PublishAction}
| {unpublish: UnpublishAction}

/**
* Creates a new draft document. The published version of the document must not already exist.
* If the draft version of the document already exists the action will fail by default, but
* this can be adjusted to instead leave the existing document in place.
*
* @public
*/
export type CreateAction = {
actionType: 'sanity.action.document.create'

/**
* ID of the published document to create a draft for.
*/
Expand All @@ -548,10 +548,10 @@ export type CreateAction = {
/**
* Replaces an existing draft document.
* At least one of the draft or published versions of the document must exist.
*
* @public
*/
export type ReplaceDraftAction = {
actionType: 'sanity.action.document.replaceDraft'

/**
* Draft document ID to replace, if it exists.
*/
Expand All @@ -572,10 +572,10 @@ export type ReplaceDraftAction = {
* Modifies an existing draft document.
* It applies the given patch to the document referenced by draftId.
* If there is no such document then one is created using the current state of the published version and then that is updated accordingly.
*
* @public
*/
export type EditAction = {
actionType: 'sanity.action.document.edit'

/**
* Draft document ID to edit
*/
Expand All @@ -596,10 +596,10 @@ export type EditAction = {
* Deletes the published version of a document and optionally some (likely all known) draft versions.
* If any draft version exists that is not specified for deletion this is an error.
* If the purge flag is set then the document history is also deleted.
*
* @public
*/
export type DeleteAction = {
actionType: 'sanity.action.document.delete'

/**
* Published document ID to delete
*/
Expand All @@ -619,10 +619,10 @@ export type DeleteAction = {
/**
* Delete the draft version of a document.
* It is an error if it does not exist. If the purge flag is set, the document history is also deleted.
*
* @public
*/
export type DiscardAction = {
actionType: 'sanity.action.document.discard'

/**
* Draft document ID to delete
*/
Expand All @@ -640,10 +640,10 @@ export type DiscardAction = {
* In either case the draft document is deleted.
* The optional revision id parameters can be used for optimistic locking to ensure
* that the draft and/or published versions of the document have not been changed by another client.
*
* @public
*/
export type PublishAction = {
actionType: 'sanity.action.document.publish'

/**
* Draft document ID to publish
*/
Expand All @@ -669,10 +669,10 @@ export type PublishAction = {
* Retract a published document.
* If there is no draft version then this is created from the published version.
* In either case the published version is deleted.
*
* @public
*/
export type UnpublishAction = {
actionType: 'sanity.action.document.unpublish'

/**
* Draft document ID to replace the published document with
*/
Expand Down
159 changes: 159 additions & 0 deletions test/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import fs from 'node:fs'
import path from 'node:path'

import {
Action,
BaseActionOptions,
type ClientConfig,
ClientError,
ContentSourceMap,
Expand Down Expand Up @@ -1463,6 +1465,163 @@ describe('client', async () => {
},
)

test.skipIf(isEdge)('action() performs single operation', async () => {
const action: Action = {
create: {
publishedId: 'post1',
attributes: {_id: 'post1', _type: 'post'},
ifExists: 'fail',
},
}

nock(projectHost())
.post('/v1/data/actions/foo', {
actions: [{actionType: 'sanity.action.document.create', ...action.create}],
})
.reply(200, {
transactionId: 'foo',
})

await expect(getClient().action(action)).resolves.not.toThrow()
})

test.skipIf(isEdge)('action() performs multiple operations', async () => {
const action1: Action = {
create: {
publishedId: 'post1',
attributes: {_id: 'post1', _type: 'post'},
ifExists: 'fail',
},
}

const action2: Action = {
replaceDraft: {
draftId: 'drafts.post2',
publishedId: 'post2',
attributes: {_id: 'post2', _type: 'post'},
},
}

const action3: Action = {
edit: {
draftId: 'drafts.post3',
publishedId: 'post3',
patch: {
set: {count: 1},
},
},
}

const action4: Action = {
delete: {
publishedId: 'post4',
includeDrafts: ['drafts.post4'],
purge: true,
},
}

const action5: Action = {
discard: {
draftId: 'drafts.post5',
purge: true,
},
}

const action6: Action = {
publish: {
draftId: 'drafts.post6',
ifDraftRevisionId: 'rev7',
publishedId: 'post6',
ifPublishedRevisionId: 'rev6',
},
}

const action7: Action = {
unpublish: {
draftId: 'drafts.post7',
publishedId: 'post7',
},
}

nock(projectHost())
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: TS is wrong here, it is not able to infer the correct type for
// edit action patch interface.
.post('/v1/data/actions/foo', {
actions: [
{actionType: 'sanity.action.document.create', ...action1.create},
{actionType: 'sanity.action.document.replaceDraft', ...action2.replaceDraft},
{actionType: 'sanity.action.document.edit', ...action3.edit},
{actionType: 'sanity.action.document.delete', ...action4.delete},
{actionType: 'sanity.action.document.discard', ...action5.discard},
{actionType: 'sanity.action.document.publish', ...action6.publish},
{actionType: 'sanity.action.document.unpublish', ...action7.unpublish},
],
})
.reply(200, {
transactionId: 'foo',
})

await expect(
getClient().action([action1, action2, action3, action4, action5, action6, action7]),
).resolves.not.toThrow()
})

test.skipIf(isEdge)('action() accepts optional parameters', async () => {
const action: Action = {
create: {
publishedId: 'post1',
attributes: {_id: 'post1', _type: 'post'},
ifExists: 'fail',
},
}

const options: BaseActionOptions = {
transactionId: 'txn1',
skipCrossDatasetReferenceValidation: true,
dryRun: true,
}

nock(projectHost())
.post('/v1/data/actions/foo', {
actions: [{actionType: 'sanity.action.document.create', ...action.create}],
transactionId: 'txn1',
skipCrossDatasetReferenceValidation: true,
dryRun: true,
})
.reply(200, {
transactionId: 'txn1',
})

await expect(getClient().action(action, options)).resolves.not.toThrow()
})

test.skipIf(isEdge)('action() handles undefined optional parameters gracefully', async () => {
const action: Action = {
create: {
publishedId: 'post1',
attributes: {_id: 'post1', _type: 'post'},
ifExists: 'fail',
},
}

const options: BaseActionOptions = {
transactionId: undefined,
skipCrossDatasetReferenceValidation: undefined,
dryRun: undefined,
}

nock(projectHost())
.post('/v1/data/actions/foo', {
actions: [{actionType: 'sanity.action.document.create', ...action.create}],
})
.reply(200, {
transactionId: 'foo',
})

await expect(getClient().action(action, options)).resolves.not.toThrow()
})

test.skipIf(isEdge)('uses GET for queries below limit', async () => {
// Please dont ever do this. Just... don't.
const clause: string[] = []
Expand Down

0 comments on commit 5c8e6f2

Please sign in to comment.