NewsSourceIngestedPayload news.source.ingested@1

Generated at from openvibe-contracts v0.34.2 and openvibe-sdk v0.5.0.

Version
1.0.0
Owner
news
Visibility
first-party
Status
active
Compatibility
backward
Schema
https://openvibe.network/contracts/events/payloads/news.source.ingested.v1.json

news.source.ingested v1 (OpenVibe.News server/domain/ingest.js ingestedEvent, from apply and applyRemoval). An OpenVibe.Sources item of category news changed what News holds, in the transaction that applied it: created (a new news_source_items row, with its dedupe outcome and the cluster it joined; news.cluster.updated follows in the same transaction), updated (Sources sent a newer revision of an item News has; adds previous_revision) or removed (Sources removed the item: sticky, the stored summary is dropped; adds reason and origin). Items arrive through the Events webhook (sources.item.*) or the cursor pull; a replay, an older or equal revision, an item of another category, an update to a removed item and a removal of an item never ingested emit nothing, and an item without a title is news.source.failed (state rejected) instead. Deliberately left out: the headline, outlet, authors, published_at and licensed summary (GET /api/v1/source-items/:id with news.cluster.read) and the cluster_reason explanation. Envelope: subject { type: source_item, id: nsi_… }, visibility internal, priority low, actor service:news.

Fields

FieldTypeRequiredDescriptionConstraints
item_idstringyesNews' own source item (news_source_items).
  • pattern ^nsi_[0-9A-HJKMNP-TV-Z]{26}$
actionenumyes
  • one of "created", "updated", "removed"
statusenumyesThe item after the change: active or duplicate for created and updated, removed exactly when action is removed.
  • one of "active", "duplicate", "removed"
sources_itemobjectyesTyped reference (common/entity-ref@1 shape) to the OpenVibe.Sources item and the Sources revision News now holds.
  • no other fields
sources_item.serviceconstyes
  • = "sources"
sources_item.typeconstyes
  • = "item"
sources_item.idstringyesThe Sources item id (itm_…), as Sources gave it.
  • minLength 1
sources_item.revisionintegeryesSources revision the stored fields came from (1 when Sources sent none).
source_keystringyesThe Sources source the item came from, as Sources gave it ('unknown' when it gave none).
  • minLength 1
canonical_urlstring | nullyesThe item's canonical URL as Sources gave it (not validated by News); null when it had none.
duplicate_ofstring | nullyesThe original item when status is duplicate (the root of any duplicate chain); null otherwise.
  • pattern ^nsi_[0-9A-HJKMNP-TV-Z]{26}$
dedupe_ruleenumyesWhich dedupe rule matched when the item was first ingested: same normalised URL, same Sources content hash, or a near-identical headline within 48 h; null when it is not a duplicate.
  • one of "canonical_url", "content_hash", "near_title", null
cluster_idstring | nullyesThe cluster the item belongs to (every ingested item is placed in one).
  • pattern ^clu_[0-9A-HJKMNP-TV-Z]{26}$
previous_revisionintegeraction updated only: the Sources revision News held before.
reasonstringaction removed only: why Sources removed it ('removed upstream' when it gave no reason).
  • minLength 1
  • maxLength 500
originenumaction removed only: whether the removal came as a sources.item.removed delivery or through the cursor pull.
  • one of "webhook", "pull"

Examples

From the contract's own test fixtures: valid ones validate, rejected ones must fail.

Valid: created-duplicate
{
  "item_id": "nsi_01K5RZ4F0A6H3K8PQ2S5W7Y9ZE",
  "action": "created",
  "status": "duplicate",
  "sources_item": {
    "service": "sources",
    "type": "item",
    "id": "itm_01K5RZ3X8Y0Z2B4D6F8H0K2M4P",
    "revision": 1
  },
  "source_key": "paper-b",
  "canonical_url": "https://www.wire-a.example/space/europa-clipper-launch/?utm_source=feed",
  "duplicate_of": "nsi_01K5RYX2C4E6G8J0M2P4R6T8VA",
  "dedupe_rule": "canonical_url",
  "cluster_id": "clu_01K5RZ3M8R5D1F7H3K9N2Q4S6T"
}
Valid: created
{
  "item_id": "nsi_01K5RZ3M8Q2W7T4XC9V6B1N0JD",
  "action": "created",
  "status": "active",
  "sources_item": {
    "service": "sources",
    "type": "item",
    "id": "itm_01K5RZ2V7W9Y1A3C5E7G9J1K3M",
    "revision": 1
  },
  "source_key": "wire-a",
  "canonical_url": "https://wire-a.example/space/europa-clipper-launch",
  "duplicate_of": null,
  "dedupe_rule": null,
  "cluster_id": "clu_01K5RZ3M8R5D1F7H3K9N2Q4S6T"
}
Valid: removed
{
  "item_id": "nsi_01K5RZ3M8Q2W7T4XC9V6B1N0JD",
  "action": "removed",
  "status": "removed",
  "sources_item": {
    "service": "sources",
    "type": "item",
    "id": "itm_01K5RZ2V7W9Y1A3C5E7G9J1K3M",
    "revision": 3
  },
  "source_key": "wire-a",
  "canonical_url": null,
  "duplicate_of": null,
  "dedupe_rule": null,
  "cluster_id": "clu_01K5RZ3M8R5D1F7H3K9N2Q4S6T",
  "reason": "licence withdrawn by the publisher",
  "origin": "webhook"
}
Valid: updated
{
  "item_id": "nsi_01K5RZ3M8Q2W7T4XC9V6B1N0JD",
  "action": "updated",
  "status": "active",
  "sources_item": {
    "service": "sources",
    "type": "item",
    "id": "itm_01K5RZ2V7W9Y1A3C5E7G9J1K3M",
    "revision": 2
  },
  "source_key": "wire-a",
  "canonical_url": "https://wire-a.example/space/europa-clipper-launch",
  "duplicate_of": null,
  "dedupe_rule": null,
  "cluster_id": "clu_01K5RZ3M8R5D1F7H3K9N2Q4S6T",
  "previous_revision": 1
}
Rejected: bad-item-id
{
  "item_id": "itm_01K5RZ2V7W9Y1A3C5E7G9J1K3M",
  "action": "created",
  "status": "active",
  "sources_item": {
    "service": "sources",
    "type": "item",
    "id": "itm_01K5RZ2V7W9Y1A3C5E7G9J1K3M",
    "revision": 1
  },
  "source_key": "wire-a",
  "canonical_url": null,
  "duplicate_of": null,
  "dedupe_rule": null,
  "cluster_id": "clu_01K5RZ3M8R5D1F7H3K9N2Q4S6T"
}
Rejected: headline-leaked
{
  "item_id": "nsi_01K5RZ3M8Q2W7T4XC9V6B1N0JD",
  "action": "created",
  "status": "active",
  "sources_item": {
    "service": "sources",
    "type": "item",
    "id": "itm_01K5RZ2V7W9Y1A3C5E7G9J1K3M",
    "revision": 1
  },
  "source_key": "wire-a",
  "canonical_url": null,
  "duplicate_of": null,
  "dedupe_rule": null,
  "cluster_id": "clu_01K5RZ3M8R5D1F7H3K9N2Q4S6T",
  "headline": "NASA launches Europa Clipper"
}
Rejected: missing-sources-item
{
  "item_id": "nsi_01K5RZ3M8Q2W7T4XC9V6B1N0JD",
  "action": "created",
  "status": "active",
  "source_key": "wire-a",
  "canonical_url": null,
  "duplicate_of": null,
  "dedupe_rule": null,
  "cluster_id": "clu_01K5RZ3M8R5D1F7H3K9N2Q4S6T"
}
Rejected: unknown-dedupe-rule
{
  "item_id": "nsi_01K5RZ4F0A6H3K8PQ2S5W7Y9ZE",
  "action": "created",
  "status": "duplicate",
  "sources_item": {
    "service": "sources",
    "type": "item",
    "id": "itm_01K5RZ3X8Y0Z2B4D6F8H0K2M4P",
    "revision": 1
  },
  "source_key": "paper-b",
  "canonical_url": null,
  "duplicate_of": "nsi_01K5RYX2C4E6G8J0M2P4R6T8VA",
  "dedupe_rule": "fuzzy",
  "cluster_id": "clu_01K5RZ3M8R5D1F7H3K9N2Q4S6T"
}

Validate

const contracts = require('openvibe-contracts');
contracts.validate('news.source.ingested@1', value);   // { valid, errors: [{ path, message }] }