From 2a0dca62439904e03f58d5e102da93d6f25adade Mon Sep 17 00:00:00 2001 From: Francisco Solla Date: Wed, 23 Sep 2026 11:52:32 -0300 Subject: [PATCH] =?UTF-8?q?fix(S29):=20achados=20da=20review=20=E2=80=94?= =?UTF-8?q?=20snapshot=20p=C3=B3s-rebase,=20manifest=20e2e=20e=20a11y=20do?= =?UTF-8?q?=20an=C3=BAncio?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - snapshot drizzle da migration regenerado pós-rebase (inclui as tabelas do S27; sem ele o próximo migrate:create recriaria content_piece) - manifest e2e: CampaignFooter/jingleReads/campaignTime acordam o frontendShareLink; dynamicParams inerte removido da rota .ics - anúncio: dl válido (Local sem dd), aria-live só no status/chip, sem hasImage/cast; hook direct deixa o erro de URL falar antes (+ teste int) - débitos triados no impl plan (folding .ics -> #1281; deferidos com gatilho) Related #1268 --- ...ilhamento-destino-trocavel-anuncio-impl.md | 9 +- scripts/lib/e2e-affected-manifest.mjs | 15 +- src/app/(frontend)/[type]/evento.ics/route.ts | 2 - src/collections/ShareLink.ts | 12 +- .../shareLink/ShareLinkAnnouncement.tsx | 29 +- src/lib/calendarEvent.ts | 10 +- ...23_054145_add_share_link_announcement.json | 848 +++++++++++++++++- tests/int/shareLink.int.spec.ts | 12 + 8 files changed, 907 insertions(+), 30 deletions(-) diff --git a/docs/plans/link-compartilhamento-destino-trocavel-anuncio-impl.md b/docs/plans/link-compartilhamento-destino-trocavel-anuncio-impl.md index 2556846c..29b45f1b 100644 --- a/docs/plans/link-compartilhamento-destino-trocavel-anuncio-impl.md +++ b/docs/plans/link-compartilhamento-destino-trocavel-anuncio-impl.md @@ -100,7 +100,7 @@ Não se aplica — a intenção não apresenta dado agregado (sem contagem/analy - [ ] Aceite de produto da intenção ainda coberto (modo por link; pool com rótulo; direct sem no-ar recusado; anúncio com data/local; Entrar desativado→ativo ~1 min; agenda Google+.ics com data e sem data esconde o botão; compartilha o link curto; despublicado 404; nada hardcoded para 03/10) - [ ] Invariantes AGENTS/engineering-standards (URL pública intacta; S19 sem mudança de comportamento; fail-closed/noindex; sem Consent/PII; schema só por migration `push:false`; `overrideAccess` documentado; lib→utilities→components→app; pt-BR em copy, inglês em identificador) -- [ ] Testes de domínio previstos (unit/int) onde access/write paths mudam — obrigatórios: unit `shareLink` (modo/live/`shareLinkIcsPath`), unit `calendarEvent` (Google/ICS/2h), unit `shareLinkAnnouncement` (view + ilha de ativação jsdom), unit `contentShare` (`event`); int `shareLink` (hook cross-field com mensagens, loader publicado, live target fail-closed). Discricionários: admin e2e do checkbox; ETag do `.ics`. E2E obrigatório estendendo `frontendShareLink`: página de anúncio 200/noindex/sem refresh, `.ics` 200/404, redirect pós-live, e a regressão S19 (redirect/meta/no-JS/404/republicação) — o spec já é do conjunto curado. +- [ ] Testes de domínio previstos (unit/int) onde access/write paths mudam — obrigatórios: unit `shareLink` (modo/live/`shareLinkIcsPath`), unit `calendarEvent` (Google/ICS/2h), unit `shareLinkAnnouncement` (view model), unit `contentShare` (`event`); int `shareLink` (hook cross-field com mensagens, loader publicado, live target fail-closed). Discricionários: admin e2e do checkbox; ETag do `.ics`. E2E obrigatório estendendo `frontendShareLink`: página de anúncio 200/noindex/sem refresh, `.ics` 200/404, redirect pós-live, e a regressão S19 (redirect/meta/no-JS/404/republicação) — o spec já é do conjunto curado. A **ilha de ativação** é coberta por e2e (client real, prod e dev) em vez de unit jsdom. ## Self-score (decision-quality) @@ -111,3 +111,10 @@ Não se aplica — a intenção não apresenta dado agregado (sem contagem/analy 5. **Intenção satisfeita:** 5 — todos os bullets do aceite mapeados em componentes/fases/testes, incluindo a questão em aberto aprovada (B: link volta a levar direto). Média 4.8/5 — plano aprovado no modo `--auto`. + +## Débitos triados (review do PR #1280) + +- **Folding RFC 5545 (75 octets) no `.ics`** — registrado como **#1281** (`S29-FOLLOWUP`, kind chore, P3): owner `src/lib/ical.ts`, 2 call sites (`lib/calendarEvent.ts`, `utilities/calendarFeed.ts`). +- **`importMap.js` tracked apesar do OPS99** — descartado: dívida pré-existente do `main`, classe já rastreada em #772/#791 + postmortem 2026-09-12; reavaliar `git rm --cached` quando o guard do #772 fechar. +- **Rate limiting da rota `live`** — deferido: gatilho = pico anômalo de requisições na rota (mitigação barata: cache in-memory de 5–10s no loader). +- **Settle dev-only de 6s no e2e `frontendShareLink`** — deferido: gatilho = primeira flake local do spec (trocar por condição observável); o SQL cru é deliberado e documentado (isola a race de revalidação/HMR). diff --git a/scripts/lib/e2e-affected-manifest.mjs b/scripts/lib/e2e-affected-manifest.mjs index fb173366..be1536cc 100644 --- a/scripts/lib/e2e-affected-manifest.mjs +++ b/scripts/lib/e2e-affected-manifest.mjs @@ -118,6 +118,12 @@ export const E2E_AFFECTED_MANIFEST = [ prefixes: ['src/lib/contentShare.ts'], specs: ['frontend', 'frontendShareLink'], }, + { + // S29 — the announcement page and its spec derive the Bahia event label + // here (the spec imports the formatter), so a diff must wake it. + prefixes: ['src/lib/campaignTime.ts'], + specs: ['frontendShareLink'], + }, { // S21 — the public jingles page: ordered published cards, the lazy // in-page player (one at a time), the slug-based download name and the @@ -134,7 +140,9 @@ export const E2E_AFFECTED_MANIFEST = [ 'src/collections/Jingle.ts', 'src/components/jingles', ], - specs: ['frontendJingles'], + // S29 — `hasPublishedJingles` also decides the footer of the announcement + // page, so the share-link spec wakes on the same owners. + specs: ['frontendJingles', 'frontendShareLink'], }, { // S27 — the public Central de Conteúdos: the facet/term catalogue, the @@ -157,10 +165,11 @@ export const E2E_AFFECTED_MANIFEST = [ }, { // S21 — the campaign footer owns the conditional "Jingles" discovery link - // (it renders on the home, the cards page and `/jingles`). + // (it renders on the home, the cards page, `/jingles` and the S29 + // announcement page). // S27 — and the conditional "Conteúdos" link. prefixes: ['src/components/CampaignFooter.tsx'], - specs: ['frontend', 'frontendJingles', 'frontendConteudos'], + specs: ['frontend', 'frontendJingles', 'frontendConteudos', 'frontendShareLink'], }, { // S13/S30 — the personalized-cards studio lives in shared cards components diff --git a/src/app/(frontend)/[type]/evento.ics/route.ts b/src/app/(frontend)/[type]/evento.ics/route.ts index 725cc820..7b6acb1d 100644 --- a/src/app/(frontend)/[type]/evento.ics/route.ts +++ b/src/app/(frontend)/[type]/evento.ics/route.ts @@ -2,8 +2,6 @@ import { buildCalendarEventIcs } from '@/lib/calendarEvent' import { shareLinkIcsUid } from '@/lib/shareLink' import { getCachedPublishedShareLinkBySlug } from '@/utilities/shareLinkReads' -export const dynamicParams = true - type RouteParams = { type: string } /** diff --git a/src/collections/ShareLink.ts b/src/collections/ShareLink.ts index ab77893e..0f5783fe 100644 --- a/src/collections/ShareLink.ts +++ b/src/collections/ShareLink.ts @@ -12,7 +12,6 @@ import { isReservedShareLinkSlug, isValidShareLinkDestination, isValidShareLinkSlug, - resolveLiveShareLinkDestination, resolveShareLinkMode, type ShareLinkDestinationLike, } from '@/lib/shareLink' @@ -79,14 +78,15 @@ const validateShareLinkConfiguration: CollectionBeforeValidateHook = ({ ? (nextData.destinations as ShareLinkDestinationLike[]) : [] - if (destinations.filter((destination) => destination?.live).length > 1) { + const liveCount = destinations.filter((destination) => destination?.live).length + if (liveCount > 1) { throw new APIError(SHARE_LINK_LIVE_DUPLICATE_MESSAGE, 400) } - if ( - resolveShareLinkMode(nextData.mode) === 'direct' && - !resolveLiveShareLinkDestination(destinations) - ) { + // Only the absence of a flagged row is the `direct` rule's business: a live + // row with a malformed URL must surface the field's own URL message (the + // runtime read still fails closed on it). + if (resolveShareLinkMode(nextData.mode) === 'direct' && liveCount === 0) { throw new APIError(SHARE_LINK_DIRECT_WITHOUT_LIVE_MESSAGE, 400) } diff --git a/src/components/shareLink/ShareLinkAnnouncement.tsx b/src/components/shareLink/ShareLinkAnnouncement.tsx index 4b2aeaa3..259e110c 100644 --- a/src/components/shareLink/ShareLinkAnnouncement.tsx +++ b/src/components/shareLink/ShareLinkAnnouncement.tsx @@ -44,7 +44,6 @@ export const ShareLinkAnnouncement = ({ }) => { const [live, setLive] = useState(null) const isLive = live !== null - const hasImage = Boolean(view.imageUrl) const hasEventDetails = Boolean(view.eventLabel || view.location) useEffect(() => { @@ -100,13 +99,13 @@ export const ShareLinkAnnouncement = ({ className={cn( 'relative z-1 mx-auto grid max-w-6xl overflow-hidden rounded-[18px] border border-(--campaign-line) bg-white', 'shadow-[0_18px_50px_rgb(71_19_14/10%)]', - hasImage && 'lg:grid-cols-[0.95fr_1.05fr]', + view.imageUrl && 'lg:grid-cols-[0.95fr_1.05fr]', )} > - {hasImage ? ( + {view.imageUrl ? (
{view.imageAlt} +
{view.eventLabel ? ( -
+
-
+ ) : null} {view.location ? (
@@ -165,15 +164,18 @@ export const ShareLinkAnnouncement = ({ strokeWidth={2} aria-hidden="true" /> -
{view.location}
+ {view.location}
) : null} - +
) : null} -
+
{isLive && live ? ( -
+
Destino no ar {live.label || 'Destino'} @@ -204,7 +206,10 @@ export const ShareLinkAnnouncement = ({ )} {!isLive ? ( -

+

A transmissão ainda não começou.

) : null} diff --git a/src/lib/calendarEvent.ts b/src/lib/calendarEvent.ts index e0b3b9e6..3869847f 100644 --- a/src/lib/calendarEvent.ts +++ b/src/lib/calendarEvent.ts @@ -16,7 +16,7 @@ export type CalendarEventInput = { endsAt?: string | null } -const formatICSInstant = (date: Date): string => formatICalDate(date.toISOString()) +const formatInstant = (date: Date): string => formatICalDate(date.toISOString()) /** * The event window: no `startsAt` (or an invalid one) means "no event" — the @@ -49,7 +49,7 @@ export const buildGoogleCalendarEventUrl = (event: CalendarEventInput): string | const params = new URLSearchParams({ action: 'TEMPLATE', text: event.title, - dates: `${formatICSInstant(window.start)}/${formatICSInstant(window.end)}`, + dates: `${formatInstant(window.start)}/${formatInstant(window.end)}`, }) if (event.description) params.set('details', event.description) if (event.location) params.set('location', event.location) @@ -80,9 +80,9 @@ export const buildCalendarEventIcs = ({ 'METHOD:PUBLISH', 'BEGIN:VEVENT', `UID:${escapeICalText(uid)}`, - `DTSTAMP:${formatICSInstant(dtstamp)}`, - `DTSTART:${formatICSInstant(window.start)}`, - `DTEND:${formatICSInstant(window.end)}`, + `DTSTAMP:${formatInstant(dtstamp)}`, + `DTSTART:${formatInstant(window.start)}`, + `DTEND:${formatInstant(window.end)}`, `SUMMARY:${escapeICalText(event.title)}`, ] diff --git a/src/migrations/20260923_054145_add_share_link_announcement.json b/src/migrations/20260923_054145_add_share_link_announcement.json index 8c269e3a..2af03339 100644 --- a/src/migrations/20260923_054145_add_share_link_announcement.json +++ b/src/migrations/20260923_054145_add_share_link_announcement.json @@ -8362,6 +8362,699 @@ "checkConstraints": {}, "isRLSEnabled": false }, + "public.content_piece_topics": { + "name": "content_piece_topics", + "schema": "", + "columns": { + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "parent_id": { + "name": "parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "enum_content_piece_topics", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + } + }, + "indexes": { + "content_piece_topics_order_idx": { + "name": "content_piece_topics_order_idx", + "columns": [ + { + "expression": "order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "content_piece_topics_parent_idx": { + "name": "content_piece_topics_parent_idx", + "columns": [ + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "content_piece_topics_value_idx": { + "name": "content_piece_topics_value_idx", + "columns": [ + { + "expression": "value", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "content_piece_topics_parent_fk": { + "name": "content_piece_topics_parent_fk", + "tableFrom": "content_piece_topics", + "tableTo": "content_piece", + "columnsFrom": [ + "parent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.content_piece_curated_fields": { + "name": "content_piece_curated_fields", + "schema": "", + "columns": { + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "parent_id": { + "name": "parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "enum_content_piece_curated_fields", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + } + }, + "indexes": { + "content_piece_curated_fields_order_idx": { + "name": "content_piece_curated_fields_order_idx", + "columns": [ + { + "expression": "order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "content_piece_curated_fields_parent_idx": { + "name": "content_piece_curated_fields_parent_idx", + "columns": [ + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "content_piece_curated_fields_parent_fk": { + "name": "content_piece_curated_fields_parent_fk", + "tableFrom": "content_piece_curated_fields", + "tableTo": "content_piece", + "columnsFrom": [ + "parent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.content_piece": { + "name": "content_piece", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "enum_content_piece_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "municipality_id": { + "name": "municipality_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "city_label": { + "name": "city_label", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "region": { + "name": "region", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "institution": { + "name": "institution", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "piece_date": { + "name": "piece_date", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": false + }, + "duration_seconds": { + "name": "duration_seconds", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "transcript": { + "name": "transcript", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "media_id": { + "name": "media_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "source_url": { + "name": "source_url", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "origin": { + "name": "origin", + "type": "enum_content_piece_origin", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'arquivo'" + }, + "status": { + "name": "status", + "type": "enum_content_piece_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'rascunho'" + }, + "published_at": { + "name": "published_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": false + }, + "processing_status": { + "name": "processing_status", + "type": "enum_content_piece_processing_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pronto'" + }, + "step": { + "name": "step", + "type": "enum_content_piece_step", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "search_text": { + "name": "search_text", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "created_by_id": { + "name": "created_by_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "content_piece_slug_idx": { + "name": "content_piece_slug_idx", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "content_piece_type_idx": { + "name": "content_piece_type_idx", + "columns": [ + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "content_piece_municipality_idx": { + "name": "content_piece_municipality_idx", + "columns": [ + { + "expression": "municipality_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "content_piece_piece_date_idx": { + "name": "content_piece_piece_date_idx", + "columns": [ + { + "expression": "piece_date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "content_piece_media_idx": { + "name": "content_piece_media_idx", + "columns": [ + { + "expression": "media_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "content_piece_source_url_idx": { + "name": "content_piece_source_url_idx", + "columns": [ + { + "expression": "source_url", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "content_piece_status_idx": { + "name": "content_piece_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "content_piece_processing_status_idx": { + "name": "content_piece_processing_status_idx", + "columns": [ + { + "expression": "processing_status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "content_piece_created_by_idx": { + "name": "content_piece_created_by_idx", + "columns": [ + { + "expression": "created_by_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "content_piece_updated_at_idx": { + "name": "content_piece_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "content_piece_created_at_idx": { + "name": "content_piece_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "content_piece_municipality_id_municipality_id_fk": { + "name": "content_piece_municipality_id_municipality_id_fk", + "tableFrom": "content_piece", + "tableTo": "municipality", + "columnsFrom": [ + "municipality_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "content_piece_media_id_content_media_id_fk": { + "name": "content_piece_media_id_content_media_id_fk", + "tableFrom": "content_piece", + "tableTo": "content_media", + "columnsFrom": [ + "media_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "content_piece_created_by_id_campaign_user_id_fk": { + "name": "content_piece_created_by_id_campaign_user_id_fk", + "tableFrom": "content_piece", + "tableTo": "campaign_user", + "columnsFrom": [ + "created_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.content_media": { + "name": "content_media", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "alt": { + "name": "alt", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "url": { + "name": "url", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "thumbnail_u_r_l": { + "name": "thumbnail_u_r_l", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "filename": { + "name": "filename", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "mime_type": { + "name": "mime_type", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "filesize": { + "name": "filesize", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "width": { + "name": "width", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "height": { + "name": "height", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "focal_x": { + "name": "focal_x", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "focal_y": { + "name": "focal_y", + "type": "numeric", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "content_media_updated_at_idx": { + "name": "content_media_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "content_media_created_at_idx": { + "name": "content_media_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "content_media_filename_idx": { + "name": "content_media_filename_idx", + "columns": [ + { + "expression": "filename", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, "public.recording_speaker_labels": { "name": "recording_speaker_labels", "schema": "", @@ -13993,6 +14686,18 @@ "primaryKey": false, "notNull": false }, + "content_piece_id": { + "name": "content_piece_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "content_media_id": { + "name": "content_media_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, "recording_id": { "name": "recording_id", "type": "integer", @@ -14493,6 +15198,36 @@ "method": "btree", "with": {} }, + "payload_locked_documents_rels_content_piece_id_idx": { + "name": "payload_locked_documents_rels_content_piece_id_idx", + "columns": [ + { + "expression": "content_piece_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_locked_documents_rels_content_media_id_idx": { + "name": "payload_locked_documents_rels_content_media_id_idx", + "columns": [ + { + "expression": "content_media_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, "payload_locked_documents_rels_recording_id_idx": { "name": "payload_locked_documents_rels_recording_id_idx", "columns": [ @@ -15077,6 +15812,32 @@ "onDelete": "cascade", "onUpdate": "no action" }, + "payload_locked_documents_rels_content_piece_fk": { + "name": "payload_locked_documents_rels_content_piece_fk", + "tableFrom": "payload_locked_documents_rels", + "tableTo": "content_piece", + "columnsFrom": [ + "content_piece_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "payload_locked_documents_rels_content_media_fk": { + "name": "payload_locked_documents_rels_content_media_fk", + "tableFrom": "payload_locked_documents_rels", + "tableTo": "content_media", + "columnsFrom": [ + "content_media_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, "payload_locked_documents_rels_recording_fk": { "name": "payload_locked_documents_rels_recording_fk", "tableFrom": "payload_locked_documents_rels", @@ -16928,6 +17689,91 @@ "unpublished" ] }, + "public.enum_content_piece_topics": { + "name": "enum_content_piece_topics", + "schema": "public", + "values": [ + "saude", + "educacao", + "cultura", + "esporte", + "seguranca-publica", + "meio-ambiente", + "economia-trabalho", + "direitos-humanos", + "infraestrutura", + "ciencia-tecnologia", + "politica-instituicoes", + "agricultura", + "habitacao-cidades", + "comunicacao-midia", + "igualdade-racial", + "mulheres-genero", + "juventude", + "pessoa-deficiencia" + ] + }, + "public.enum_content_piece_curated_fields": { + "name": "enum_content_piece_curated_fields", + "schema": "public", + "values": [ + "title", + "description", + "topics", + "municipality", + "institution", + "pieceDate", + "transcript", + "type" + ] + }, + "public.enum_content_piece_type": { + "name": "enum_content_piece_type", + "schema": "public", + "values": [ + "video", + "foto", + "texto", + "audio", + "card" + ] + }, + "public.enum_content_piece_origin": { + "name": "enum_content_piece_origin", + "schema": "public", + "values": [ + "arquivo", + "instagram", + "youtube" + ] + }, + "public.enum_content_piece_status": { + "name": "enum_content_piece_status", + "schema": "public", + "values": [ + "rascunho", + "publicado" + ] + }, + "public.enum_content_piece_processing_status": { + "name": "enum_content_piece_processing_status", + "schema": "public", + "values": [ + "processando", + "pronto", + "falhou" + ] + }, + "public.enum_content_piece_step": { + "name": "enum_content_piece_step", + "schema": "public", + "values": [ + "extraindo", + "transcrevendo", + "catalogando", + "salvando" + ] + }, "public.enum_recording_status": { "name": "enum_recording_status", "schema": "public", @@ -17264,6 +18110,6 @@ "tables": {}, "columns": {} }, - "id": "267190ce-de8f-4bbf-8b43-de400507dc4d", + "id": "a043d1e9-f128-4742-90d5-2deb25a9b7d4", "prevId": "00000000-0000-0000-0000-000000000000" } \ No newline at end of file diff --git a/tests/int/shareLink.int.spec.ts b/tests/int/shareLink.int.spec.ts index ebd9569a..5d19df71 100644 --- a/tests/int/shareLink.int.spec.ts +++ b/tests/int/shareLink.int.spec.ts @@ -251,6 +251,18 @@ describe('shareLink', () => { }, ) + it('lets the URL field speak when a live row has a malformed URL', async () => { + const messages = await createErrorMessages({ + title: 'URL malformada no ar', + slug: 'url-malformada-no-ar-s29', + description: 'x', + mode: 'direct', + destinations: [{ label: 'Meet', url: 'meet.google.com/abc', live: true }], + }) + + expect(messages).toContain(SHARE_LINK_DESTINATION_INVALID_MESSAGE) + }) + it('rejects more than one destination on air', async () => { await expect( createShareLink({