Skip to content

[W1 — do not merge] API contract v1 - #2

Merged
andreacw5 merged 5 commits into
developfrom
claude/api-contract-v1-a7b4c3
Oct 27, 2026
Merged

andreacw5 merged 5 commits into
developfrom
claude/api-contract-v1-a7b4c3

Conversation

@andreacw5

@andreacw5 andreacw5 commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Wave W1 of platform plan docs/platform/plans/02-api-contract-cutover.md (Articuno part). Do not merge: merges only in the W4 cutover, together with fileharbor, gatherly, @heyatom/client 1.0.0 and the consumers.

Changes

  • @heyatom/nest-common ^0.3.0 → ^0.4.0; global TransformInterceptor (APP_INTERCEPTOR).
  • Every non-paginated 2xx JSON body → { data }. Paginated lists unchanged ({ data, meta }, passed through by the paginate() marker). 204s unchanged.
  • GET /articles/{id}/translations and GET /admin/articles/{id}/translations: bare array → { data: [...] }.
  • GET /admin/webhooks: the controller spread the page to map rows, dropping the marker (would have become {data:{data,meta}}); rebuilt with markPaginated, with a spec.
  • Swagger: all 2xx @ApiResponse/@ApiOkResponse/@ApiCreatedResponse({ type }) → ApiOkData / ApiCreatedData. openapi.json regenerated.
  • @NoTransform() exemptions: none (no file/stream/redirect/external-format endpoint).

Shapes changed (old X → new)

  • POST /articles → {data: ArticleDto}
  • GET /articles/{slug} → {data: ArticleDto}
  • PATCH /articles/{id} → {data: ArticleDto}
  • POST /banned-words → {data: BannedWordDto}
  • POST /articles/{id}/translations → {data: ArticleTranslationDto}
  • GET /articles/{id}/translations → {data: [ArticleTranslationSummaryDto]}
  • GET /articles/{id}/translations/{languageCode} → {data: ArticleTranslationDto}
  • PATCH /articles/{id}/translations/{languageCode} → {data: ArticleTranslationDto}
  • POST /bookmarks/{articleId} → {data: BookmarkToggleResultDto}
  • POST /categories → {data: CategoryDto}
  • GET /categories/{slug} → {data: CategoryDto}
  • PATCH /categories/{id} → {data: CategoryDto}
  • POST /comments → {data: CommentDto}
  • GET /comments/{id} → {data: CommentDto}
  • PATCH /comments/{id} → {data: CommentDto}
  • POST /interactions/articles/{articleId}/like → {data: LikeToggleResultDto}
  • GET /interactions/articles/{articleId}/status → {data: ArticleInteractionStatusDto}
  • POST /reports → {data: ReportDto}
  • PATCH /reports/{id}/status → {data: ReportDto}
  • POST /tags → {data: TagDto}
  • GET /tags/{slug} → {data: TagDto}
  • PATCH /tags/{id} → {data: TagDto}
  • POST /users/sync → {data: UserDto}
  • GET /health/live → {data: {...}} (health, body free per api.md §6)
  • GET /health/ready → {data: {...}} (health, body free per api.md §6)
  • POST /admin/articles → {data: ArticleDto}
  • GET /admin/articles/{key} → {data: ArticleDto}
  • PATCH /admin/articles/{id} → {data: ArticleDto}
  • POST /admin/articles/{id}/translations → {data: ArticleTranslationDto}
  • GET /admin/articles/{id}/translations → {data: [ArticleTranslationSummaryDto]}
  • GET /admin/articles/{id}/translations/{languageCode} → {data: ArticleTranslationDto}
  • PATCH /admin/articles/{id}/translations/{languageCode} → {data: ArticleTranslationDto}
  • POST /admin/tags → {data: TagDto}
  • GET /admin/tags/{id} → {data: TagDto}
  • PATCH /admin/tags/{id} → {data: TagDto}
  • POST /admin/categories → {data: CategoryDto}
  • GET /admin/categories/{id} → {data: CategoryDto}
  • PATCH /admin/categories/{id} → {data: CategoryDto}
  • POST /admin/banned-words → {data: BannedWordDto}
  • POST /admin/reports → {data: ReportDto}
  • GET /admin/reports/{id} → {data: ReportDto}
  • PATCH /admin/reports/{id}/status → {data: ReportDto}
  • GET /admin/audits/{id} → {data: AuditLogDto}
  • GET /admin/users/{id} → {data: UserListItemDto}
  • PATCH /admin/users/{id}/status → {data: UserListItemDto}
  • GET /admin/comments/{id} → {data: CommentDto}
  • PATCH /admin/comments/{id} → {data: CommentDto}
  • GET /admin/webhooks/{id} → {data: WebhookEventDto}
  • POST /admin/webhooks/{id}/resend → {data: WebhookEventDto}
  • POST /admin/webhooks/resend-all-failed → {data: WebhookResendAllResultDto}
  • GET /admin/stats/dashboard → {data: DashboardStatsDto}

Checks

  • command grep -rnE "@Api(Ok|Created)Response\(\{ type" src → empty
  • build, lint OK; specs: admin controllers, app module, new admin-webhooks spec

W3 — FileHarbor via @heyatom/client

  • src/modules/fileharbor/: HTTP moves from @nestjs/axios + form-data to FileHarborClient (@heyatom/client/fileharbor), one client per call (endpoint and API key are per tenant, so not the global Nest module). FileHarborService keeps its policy: MIME/size checks, endpoint check, id extraction, 404 on delete = already deleted.
  • Mapping: POST {endpoint}/images (form description, creatorId) → uploadImage(file, { description }, { userId }) → .data.fullPath (FileHarbor's new { data } envelope); DELETE {endpoint}/images/:id → deleteImage(id); error.response?.status === 404 → HeyAtomHttpError.status === 404.
  • The old axios options were deliberate, so the client gets fileHarborFetch: no redirects (redirect: 'manual', the tenant key never follows a Location) and a 1 MB response cap; timeouts stay 15 s (delete) / 30 s (upload). Their test moves from webhook-http.spec.ts into fileharbor.service.spec.ts.
  • Removed: fileharbor/dto/file-harbor.dto.ts, webhook/webhook-http.spec.ts, deps @nestjs/axios, axios, form-data. No env change (Articuno never had FILEHARBOR_URL / FILEHARBOR_API_KEY).
  • @heyatom/client is file:/Users/atombolato/Developer/heyatom/heyatom-client until the W4 release: CI cannot install it, red until then (expected).

Note: the branch also carries 6536961 docs(brand): add Articuno logo kit, which was already on it and is not on develop.

🤖 Generated with Claude Code

andreacw5 and others added 2 commits October 25, 2026 16:53
New mark from scratch: a pen nib pointing up that is also an A (slit and
breather hole are the counter, the open feet are the legs). Built like the
Bastion and Herald kits: generate.py rebuilds every SVG, PNG, web icon and
Stream Deck key; README covers construction, palette (orange #E27A43,
dark orange #BE551D on light, ink on the orange tile), clear space and
minimum sizes. app_logo.png is now the white lockup.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Register nest-common's TransformInterceptor globally (bump to ^0.4.0):
single resources, creations and non-paginated lists become { data },
paginate() pages stay { data, meta }. GET article-translations goes from a
bare array to { data: [...] }. No @NoTransform() exemption is needed.

The admin webhook list spread the page to map its rows, which dropped
paginate()'s marker and would have wrapped it twice: rebuilt with
markPaginated.

Swagger documents the envelope: @ApiResponse/@ApiOkResponse/
@ApiCreatedResponse({ type }) on 2xx -> ApiOkData / ApiCreatedData.
openapi.json regenerated.

BREAKING CHANGE: every non-paginated 2xx JSON body is now wrapped in
{ data }. Do not merge before the W4 cutover (platform plan 02).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
andreacw5 and others added 3 commits October 25, 2026 18:50
FileHarborService keeps its policy (MIME/size checks, endpoint check, id
extraction, 404 = already deleted); upload and delete now go through
FileHarborClient from @heyatom/client/fileharbor, one per call because
endpoint and API key are per tenant. Uploads read the { data } envelope of
FileHarbor's new contract. creatorId form field -> X-User-Id (userId).

The axios options this replaces were deliberate, so the client gets
fileHarborFetch: no redirects (the tenant key must not follow a Location)
and a 1 MB response cap; timeouts stay 15 s / 30 s. Their spec moves from
webhook-http.spec.ts into the fileharbor spec.

Drops @nestjs/axios, axios, form-data and the hand-written image DTO.
@heyatom/client points at the local checkout (file:) until the W4 release.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@andreacw5
andreacw5 marked this pull request as ready for review October 27, 2026 13:42
@andreacw5
andreacw5 merged commit 4a415d3 into develop Oct 27, 2026
2 checks passed
@andreacw5
andreacw5 deleted the claude/api-contract-v1-a7b4c3 branch October 27, 2026 13:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant