As a developer, I want to be able to create a new Wikidata item in its entirety
POST rest.php/wikibase/v0/entities/items
{ "item": { "labels": {}, [label or description in one language code is mandatory] "descriptions": {}, "aliases": {}, "sitelinks": {} "statements": {} }, "comment": ... (optional) "tags": [ ... ] (optional) "bot": false (optional) }
Acceptance criteria
At least adding a label or a description in one language is mandatory- 201 response code on success and location header in the successful creation response
- Ignore HTTP conditional request headers
- Statement errors can be handled similar to those in POST /entities/items/{item_id}/statements. Only additional change is adding the the index of the problem statement to the path context of the error message in the form property_id/index_of_erronic_statement
- Ignore id and type fields and don't consider them unexpected
- ETag, last-modified and location (URI of the newly created item) as response headers.
- Handle user authentication/authorization like in POST /entities/items/{item_id}/statements
- Client can provide additional edit metadata: mediawiki tags, edit summary text to append to the automated summary, and a bot edit flag, like in POST /entities/items/{item_id}/statements
Error cases to consider
HTTP response code | response payload | |
---|---|---|
Invalid edit tag | 400 | "code": "invalid-edit-tag" "message": "Invalid MediaWiki tag: {tag}" |
Comment too long | 400 | "code": "comment-too-long" "message": "Comment must not be longer than {limit} characters" |
Invalid data in field | 400 | "code": "item-data-invalid-field" "message": "Invalid input for '{field}'" "context": { "path": "{field}", "value": "{value}" } |
Unexpected field in item request | 400 | "code": "unexpected-field" "message": "The request body contains an unexpected field" "context": { "field": "{field}" } |
| ||
When label, description or alias fields are selected but not filled (aka, empty) | 400 | "code": "label/description/alias-list/alias-empty" "message": "Label/Description/Alias list/Alias must not be empty" "context": { "language": "{language_code}" } |
Label and description have same value | 400 | "code": "label-description-same-value" "message": "Label and description for language '{language_code}' can not have the same value" "context": { "language": "{language}" } |
Label/description/alias too long | 400 | "code": "label/description/alias-too-long" "message": "Label/Description/Alias must be no more than {limit} characters long" "context": { "language": "{language_code}", "character-limit": "{limit}" } |
Label/description/alias invalid | 400 | "code": "invalid-label/description/alias" "message": "Not a valid label/description/alias: {label/desc/alias}" "context": { "language": "{language_code}" } |
Alias type invalid | 400 | "code": "invalid-alias-list" "message": "Not a valid alias list" "context": { "language": "{language_code}" } |
Invalid language code | 400 | "code": "invalid-language-code" "message": "Not a valid language code: {language_code}" "context": { "path": "{field}", "language": "{language_code}" } |
Duplicate alias | 400 | "code": "duplicate-alias" "message": "Alias list contains a duplicate alias: '{alias}'" "context": { "language": "{language_code}", "alias": "{alias}" } |
Item with label and description already exists | 400 | "code": "item-label-description-duplicate" "message": "Item '{duplicate_item_id}' already has label '{label}' associated with language code '{language}', using the same description text" "context": { "language": "{language}", "label": "{label}", "description": "{description}", "matching-item-id": "{duplicate_item_id}" } |
Sitelink conflict | 409 | "code": "sitelink-conflict" "message": "Sitelink is already being used on {other_item_id}" "context": { "site-id": "{site_id}", "matching-item-id": "{duplicate_item_id}" } |
Sitelink title field not provided | 400 | "code": "sitelink-data-missing-title" "message": "Mandatory sitelink title missing" "context": { "site-id": "{site_id}" } |
title is empty | 400 | "code": "title-field-empty" "message": "Title must not be empty" "context": { "site-id": "{site_id}" } |
Invalid title | 400 | "code": "invalid-title-field" "message": "Not a valid input for title field" "context": { "site-id": "{site_id}" } |
Value provided as a badge is not an item ID | 400 | "code": "invalid-input-sitelink-badge" "message": "Badge input is not an item ID: {value}" "context": { "badge": "{value}", "site-id": "{site_id}" } |
Item provided is not allowed as a sitelink badge | 400 | "code": "item-not-a-badge" "message": "Item ID provided as badge is not allowed as a badge: {item_id}" "context": { "badge": "{item_id}", "site-id": "{site_id}" } |
value of badges field is not a list | 400 | "code": "invalid-sitelink-badges-format" "message": "Value of badges field is not a list" "context": { "site-id": "{site_id}" } |
Title does not exist on the given site | 400 | "code": "title-does-not-exist" "message": "Page with title {title} does not exist on the given site" "context": { "site-id": "{site_id}" } |
Invalid site ID | 400 | "code": "invalid-site-ID" "message": "Not a valid site ID:'{site_id}'" "context": { "site-id": "{site_id}" } |
Invalid statement group object | 400 | "code": "invalid-statement-group-type" "message": "Not a valid statement group" "context": { "path": "{property_id}" } |
Invalid statement object | 400 | "code": "invalid-statement-type" "message": "Not a valid statement type" "context": { "path": "{property_id/index}" } |
Statement's Property ID value does not match the key of the statement group | 400 | "code": "statement-group-property-id-mismatch" "message": "Statement's Property ID does not match the statement group key" "context": { "path": "{property_id_key}/{index}/property/id", "statement-group-property-id": "{property_id_key}", "statement-property-id": "{property_id_value}" } |
Notes
- Automated edit summary to be of the type: /* wbeditentity-create-item:0| */
- How Action API does it: https://www.wikidata.org/w/api.php?action=help&modules=wbeditentity
Task breakdown
- add to OAS
- create ItemDeserializer (plugs together all the existing deserializers for labels, descriptions, aliases, statements, sitelinks)
- create ItemCreator interface with a create( Item $item, EditMetadata $editMetadata ): ItemRevision; and implementation. We can likely reuse the existing EntityUpdater class which might then need a better name.
- happy path
- include revision ID and last modified date
- edit summary
- authorization
- add new method to PermissionChecker::canCreateItem( User $user ): bool and implement it
- top-level field validation
- covers item-data-invalid-field, unexpected-field, missing-labels-and-descriptions
- field validation
- apply middlewares (the usual ones except the one for conditional requests)
- spec tests
- mark production ready