UNPKG

wikibase-edit

Version:

Edit Wikibase from NodeJS

141 lines 6.47 kB
import { isEqual } from 'lodash-es'; import { simplifyReferences, simplifySnak } from 'wikibase-sdk'; import { isMatchingClaimFactory } from '../claim/is_matching_claim.js'; import { isMatchingSnak } from '../claim/is_matching_snak.js'; import { newError } from '../error.js'; import { validateReconciliationObject } from './validate_reconciliation_object.js'; // Ignoring MediaInfo statements weirdness here, as it doesn't rely on snaks datatypes export function reconcileClaimFactory(reconciliation, existingPropertyClaims) { return function reconcileClaim(claim) { reconciliation = 'reconciliation' in claim ? claim.reconciliation : reconciliation; if (!reconciliation) return claim; validateReconciliationObject(reconciliation, claim); const { mode, matchingQualifiers, matchingReferences } = reconciliation; if (mode === 'skip-on-any-value') { if (existingPropertyClaims.length > 0) { console.warn(`[wikibase-edit] skipping claim: a claim already exists for that property\n${JSON.stringify({ claim, existingPropertyClaims })}`); return; } } const existingClaims = existingPropertyClaims.filter(isMatchingClaimFactory(claim, matchingQualifiers)); if (claim.remove) { if (existingClaims.length > 0) { return existingClaims.map(({ id }) => ({ id, remove: true })); } else { throw newError("can't remove claim: claim not found", claim); } } if (existingClaims.length === 0) return claim; if (mode === 'skip-on-value-match') { console.warn(`[wikibase-edit] skipping claim: a similar claim already exists\n${JSON.stringify({ claim, existingClaims })}`); } else if (mode === 'merge') { if (existingClaims.length > 1) { throw newError('too many matching claims found', { claim, existingClaims }); } const existingClaim = existingClaims[0]; if (claim.qualifiers != null) { existingClaim.qualifiers = existingClaim.qualifiers || {}; addMissingQualifiers(existingClaim.qualifiers, claim.qualifiers); } if (claim.references != null) { existingClaim.references = existingClaim.references || []; if (matchingReferences) { mergeReferences(existingClaim.references, claim.references, matchingReferences); } else { const currentReference = simplifyReferences(existingClaim.references); // @ts-expect-error const newReferenceReference = claim.references.filter(isNewReference(currentReference)); existingClaim.references.push(...newReferenceReference); } } // @ts-expect-error return existingClaim; } else { throw newError('unexpected reconciliation mode', 500, { reconciliation }); } }; } function addMissingQualifiers(existingQualifiers, newQualifiers) { for (const property in newQualifiers) { existingQualifiers[property] = existingQualifiers[property] || []; existingQualifiers[property].push(...newQualifiers[property]); } } const isNewReference = currentReference => reference => { const simplifiedReference = aggregateReferenceSnaks(reference.snaks, true); return !currentReference.some(currentReference => { return isEqual(currentReference, simplifiedReference); }); }; function mergeReferences(existingReferences, newReferences, matchingReferences) { const addedReferences = []; for (const newReference of newReferences) { const newReferenceSnaks = aggregateReferenceSnaks(newReference.snaks, false); const matchingReference = existingReferences.find(isMatchingReference(newReferenceSnaks, matchingReferences)); if (matchingReference) { mergeMatchingReference(matchingReference, newReferenceSnaks); } else { addedReferences.push(newReference); } } existingReferences.push(...addedReferences); } function mergeMatchingReference(matchingReference, newReferenceSnaks) { for (const property in newReferenceSnaks) { if (property in matchingReference.snaks) { const existingPropertySnaks = matchingReference.snaks[property]; const newSnaks = newReferenceSnaks[property] .filter(snak => !hasSomeMatch(existingPropertySnaks, snak)); matchingReference.snaks[property].push(...newSnaks); } else { matchingReference.snaks[property] = newReferenceSnaks[property]; } } } const isMatchingReference = (newReferenceSnaks, matchingReferences) => existingReference => { const existingReferenceSnaks = existingReference.snaks; for (const property of matchingReferences) { const [pid, option = 'all'] = property.split(':'); const newPropertySnaks = newReferenceSnaks[pid]; const existingPropertySnaks = existingReferenceSnaks[pid]; if (newPropertySnaks == null && existingPropertySnaks == null) return false; if (newPropertySnaks != null && existingPropertySnaks == null) return false; if (newPropertySnaks == null && existingPropertySnaks != null) return false; const methodName = methodNameByOption[option]; const everyNewSnakHasAnExistingMatch = newPropertySnaks[methodName](snak => { return hasSomeMatch(existingPropertySnaks, snak); }); if (!everyNewSnakHasAnExistingMatch) return false; } return true; }; const methodNameByOption = { any: 'some', all: 'every', }; function aggregateReferenceSnaks(snaksArray, simplify) { return snaksArray.reduce((aggregatedSnaks, snak) => { const { property } = snak; aggregatedSnaks[property] = aggregatedSnaks[property] || []; if (simplify) snak = simplifySnak(snak, {}); aggregatedSnaks[property].push(snak); return aggregatedSnaks; }, {}); } function hasSomeMatch(existingPropertySnaks, snak) { return existingPropertySnaks.some(existingSnak => isMatchingSnak(existingSnak, snak)); } //# sourceMappingURL=reconcile_claim.js.map