wikibase-edit
Version:
Edit Wikibase from NodeJS
143 lines (124 loc) • 5.92 kB
text/typescript
import { isGuid, isEntityId, isPropertyId, getEntityIdFromGuid, type Guid, type PropertyClaimsId, type PropertyId, type SimplifiedClaim, type Claim, type Statement, type MediaInfoId } from 'wikibase-sdk'
import { newError } from '../error.js'
import { getEntityClaims } from '../get_entity.js'
import { formatClaimValue } from './format_claim_value.js'
import { findClaimByGuid } from './helpers.js'
import { propertiesDatatypesDontMatch } from './move_commons.js'
import { buildSnak } from './snak.js'
import type { EditEntityRawModeParams, EditEntityResponse } from '../entity/edit.js'
import type { WikibaseEditAPI } from '../index.js'
import type { BaseRevId } from '../types/common.js'
import type { SerializedConfig } from '../types/config.js'
import type { RawEditableEntity } from '../types/edit_entity.js'
// Disabling MediaInfo for now, has this function needs access to snaks datatypes
// which aren't provided on MediaInfo statements
export type MovableEntityId = Exclude<RawEditableEntity['id'], MediaInfoId>
export interface MoveClaimParams {
guid?: Guid<MovableEntityId>
propertyClaimsId?: PropertyClaimsId
id?: MovableEntityId
property?: PropertyId
newValue?: SimplifiedClaim
summary?: string
baserevid?: BaseRevId
}
export async function moveClaims (params: MoveClaimParams, config: SerializedConfig, API: WikibaseEditAPI) {
const { guid, propertyClaimsId, id: targetEntityId, property: targetPropertyId, newValue, baserevid } = params
const { instance } = config
let originEntityId: MovableEntityId
let originPropertyId: PropertyId
if (guid) {
if (!isGuid(guid)) throw newError('invalid claim guid', 400, params)
originEntityId = getEntityIdFromGuid(guid) as MovableEntityId
} else if (propertyClaimsId) {
([ originEntityId, originPropertyId ] = propertyClaimsId.split('#') as [ MovableEntityId, PropertyId ])
if (!(isEntityId(originEntityId) && isPropertyId(originPropertyId))) {
throw newError('invalid property claims id', 400, params)
}
} else {
throw newError('missing claim guid or property claims id', 400, params)
}
if (!targetEntityId) throw newError('missing target entity id', 400, params)
if (!isEntityId(targetEntityId)) throw newError('invalid target entity id', 400, params)
if (!targetPropertyId) throw newError('missing property id', 400, params)
const propertyDatatype = config.properties[targetPropertyId]
const claims = await getEntityClaims(originEntityId, config)
let movedClaims: Claim[]
if (guid) {
const claim = findClaimByGuid(claims, guid) as Claim
originPropertyId = claim.mainsnak.property
movedClaims = [ claim ]
} else {
movedClaims = claims[originPropertyId] as Claim[]
if (!movedClaims) throw newError('no property claims found', 400, params)
}
if (originEntityId === targetEntityId && originPropertyId === targetPropertyId && newValue == null) {
throw newError("move operation wouldn't have any effect: same entity, same property", 400, params)
}
const { datatype: currentPropertyDatatype } = movedClaims[0].mainsnak
if (propertyDatatype !== currentPropertyDatatype) {
propertiesDatatypesDontMatch({
movedSnaks: movedClaims,
originPropertyId,
originDatatype: currentPropertyDatatype,
targetPropertyId,
targetDatatype: propertyDatatype,
instance,
})
}
const currentEntityData: EditEntityRawModeParams = {
rawMode: true,
id: originEntityId,
claims: movedClaims.map((claim: Claim | Statement) => ({ id: claim.id, remove: true })),
summary: params.summary || config.summary || generateCurrentEntitySummary(guid, originEntityId, originPropertyId, targetEntityId, targetPropertyId),
}
movedClaims.forEach(claim => {
delete claim.id
if (newValue) {
const value = formatClaimValue(propertyDatatype, newValue, instance)
claim.mainsnak = buildSnak(targetPropertyId, propertyDatatype, value, instance) as Claim['mainsnak']
} else {
claim.mainsnak.property = targetPropertyId
}
})
if (originEntityId === targetEntityId) {
currentEntityData.claims.push(...movedClaims)
currentEntityData.baserevid = baserevid
const res = await API.entity._rawEdit(currentEntityData, config)
return [ res ]
} else {
if (baserevid) throw newError('commands editing multiple entities can not have a baserevid', 400, params)
const targetEntityData: EditEntityRawModeParams = {
rawMode: true,
id: targetEntityId,
claims: movedClaims,
summary: params.summary || config.summary || generateTargetEntitySummary(guid, originEntityId, originPropertyId, targetEntityId, targetPropertyId),
}
const removeClaimsRes = await API.entity._rawEdit(currentEntityData, config)
const addClaimsRes = await API.entity._rawEdit(targetEntityData, config)
return [ removeClaimsRes, addClaimsRes ]
}
}
const generateCurrentEntitySummary = (guid, originEntityId, originPropertyId, targetEntityId, targetPropertyId) => {
if (guid) {
if (originEntityId === targetEntityId) {
return `moving a ${originPropertyId} claim to ${targetPropertyId}`
} else {
return `moving a ${originPropertyId} claim to ${targetEntityId}#${targetPropertyId}`
}
} else {
if (originEntityId === targetEntityId) {
return `moving ${originPropertyId} claims to ${targetPropertyId}`
} else {
return `moving ${originPropertyId} claims to ${targetEntityId}#${targetPropertyId}`
}
}
}
const generateTargetEntitySummary = (guid, originEntityId, originPropertyId, targetEntityId, targetPropertyId) => {
if (guid) {
return `moving a ${originEntityId}#${originPropertyId} claim from ${targetEntityId}#${targetPropertyId}`
} else {
return `moving ${originEntityId}#${originPropertyId} claims from ${targetEntityId}#${targetPropertyId}`
}
}
export type MoveClaimResponse = EditEntityResponse[]