feat: Studio 응답 봉투를 전송 경계에서 언랩한다
studio-v1.yaml v3.0.0(ADR-006)에 맞춰 계약을 재생성하고, 성공은
{success,data,meta}, 실패는 {success,error,meta} 봉투를 전송 경계에서
언랩하는 envelopeData/envelopeError validator를 도입한다. 앱·도메인
계층은 기존과 같은 payload/ProblemDetails 모양을 계속 받고,
StudioGateway 포트 시그니처는 무변경이다.
- tech-log-studio-contract-contribution.ts: envelopeData/envelopeError
도입, 18개 operation의 outputValidator를 passthrough에서 envelopeData로
교체
- studio-error-mapping.ts: 봉투 오류의 status(항상 0)를
outcome.metadata.status로 덮는다. SafeResponseMetadata.status가
실제 필드명이며(httpStatus 아님) PROBLEM outcome에서 필수 필드다
- contract.ts: 삭제된 ProblemDetails 생성 스키마를 손으로 유지 — 앱
계층·mock 게이트웨이가 그 모양을 계속 소비한다
- asset-upload-transport.ts: multipart 업로드는 일반 계약 런타임을
거치지 않는 별도 seam이지만 같은 wire 봉투를 쓴다 — envelopeData/
envelopeError를 재사용해 이 경로도 언랩한다 (브리프 파일 목록 밖의
발견, report에 기록)
- 테스트: 신규 studio-envelope-unwrap.test.ts(TDD) + 봉투 뼈대를 직접
만드는 기존 테스트(asset-upload-transport, studio-csrf-composition,
contract-generation)를 봉투 형태로 갱신
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
d84b57bb3f
commit
25a6b63d27
@@ -1,7 +1,7 @@
|
||||
openapi: 3.1.0
|
||||
info:
|
||||
title: Tech Log Studio API
|
||||
version: 2.0.0
|
||||
version: 3.0.0
|
||||
description: |
|
||||
Tech Log Studio orchestration 계약이다.
|
||||
|
||||
@@ -109,7 +109,7 @@ paths:
|
||||
tags: [Session]
|
||||
summary: 현재 Studio 세션과 CSRF 토큰을 조회한다
|
||||
responses:
|
||||
"200": { description: 인증된 Studio 세션, content: { application/json: { schema: { $ref: "#/components/schemas/StudioSession" } } } }
|
||||
"200": { description: 인증된 Studio 세션, content: { application/json: { schema: { $ref: "#/components/schemas/StudioSessionEnvelope" } } } }
|
||||
"401": { $ref: "#/components/responses/AuthenticationRequired" }
|
||||
"403": { $ref: "#/components/responses/AccessDenied" }
|
||||
"503": { $ref: "#/components/responses/StudioUnavailable" }
|
||||
@@ -123,7 +123,7 @@ paths:
|
||||
`nextAction`을 포함한 모든 workflow 상태는 서버가 계산한다.
|
||||
Frontend는 여러 endpoint를 조합해 workflow 상태를 재추론하지 않는다.
|
||||
responses:
|
||||
"200": { description: Dashboard lists and totals, content: { application/json: { schema: { $ref: "#/components/schemas/StudioDashboard" } } } }
|
||||
"200": { description: Dashboard lists and totals, content: { application/json: { schema: { $ref: "#/components/schemas/StudioDashboardEnvelope" } } } }
|
||||
"401": { $ref: "#/components/responses/AuthenticationRequired" }
|
||||
"403": { $ref: "#/components/responses/AccessDenied" }
|
||||
"503": { $ref: "#/components/responses/StudioUnavailable" }
|
||||
@@ -150,7 +150,7 @@ paths:
|
||||
- { $ref: "#/components/parameters/Cursor" }
|
||||
- { $ref: "#/components/parameters/Limit" }
|
||||
responses:
|
||||
"200": { description: Working-copy cursor page, content: { application/json: { schema: { $ref: "#/components/schemas/DocumentPage" } } } }
|
||||
"200": { description: Working-copy cursor page, content: { application/json: { schema: { $ref: "#/components/schemas/DocumentPageEnvelope" } } } }
|
||||
"400": { $ref: "#/components/responses/MalformedRequest" }
|
||||
"401": { $ref: "#/components/responses/AuthenticationRequired" }
|
||||
"403": { $ref: "#/components/responses/AccessDenied" }
|
||||
@@ -169,7 +169,7 @@ paths:
|
||||
"201":
|
||||
description: Created working copy
|
||||
headers: { Idempotency-Replayed: { $ref: "#/components/headers/IdempotencyReplayed" } }
|
||||
content: { application/json: { schema: { $ref: "#/components/schemas/WorkingCopy" } } }
|
||||
content: { application/json: { schema: { $ref: "#/components/schemas/WorkingCopyEnvelope" } } }
|
||||
"400": { $ref: "#/components/responses/MalformedRequest" }
|
||||
"401": { $ref: "#/components/responses/AuthenticationRequired" }
|
||||
"403": { $ref: "#/components/responses/AccessDenied" }
|
||||
@@ -184,7 +184,7 @@ paths:
|
||||
tags: [Documents]
|
||||
summary: Get a working copy and its current state
|
||||
responses:
|
||||
"200": { description: Working-copy detail, content: { application/json: { schema: { $ref: "#/components/schemas/WorkingCopyDetail" } } } }
|
||||
"200": { description: Working-copy detail, content: { application/json: { schema: { $ref: "#/components/schemas/WorkingCopyDetailEnvelope" } } } }
|
||||
"401": { $ref: "#/components/responses/AuthenticationRequired" }
|
||||
"403": { $ref: "#/components/responses/AccessDenied" }
|
||||
"404": { $ref: "#/components/responses/DocumentNotFound" }
|
||||
@@ -197,8 +197,8 @@ paths:
|
||||
편집 가능한 content field만 저장한다. lifecycle 전이는 명시적인 Domain
|
||||
Action이 담당한다. 저장은 Public Projection을 변경하지 않는다.
|
||||
|
||||
`expectedVersion` 불일치는 `VERSION_CONFLICT`이며 응답 `ProblemDetails`의
|
||||
`latestDocument`로 현재 상태를 함께 제공한다.
|
||||
`expectedVersion` 불일치는 `VERSION_CONFLICT`이며 응답 `error.details`
|
||||
(`VersionConflictDetails`)의 `latestDocument`로 현재 상태를 함께 제공한다.
|
||||
parameters:
|
||||
- { $ref: "#/components/parameters/IdempotencyKey" }
|
||||
- { $ref: "#/components/parameters/CsrfToken" }
|
||||
@@ -207,7 +207,7 @@ paths:
|
||||
"200":
|
||||
description: Saved working-copy detail
|
||||
headers: { Idempotency-Replayed: { $ref: "#/components/headers/IdempotencyReplayed" } }
|
||||
content: { application/json: { schema: { $ref: "#/components/schemas/WorkingCopyDetail" } } }
|
||||
content: { application/json: { schema: { $ref: "#/components/schemas/WorkingCopyDetailEnvelope" } } }
|
||||
"400": { $ref: "#/components/responses/MalformedRequest" }
|
||||
"401": { $ref: "#/components/responses/AuthenticationRequired" }
|
||||
"403": { $ref: "#/components/responses/AccessDenied" }
|
||||
@@ -244,7 +244,7 @@ paths:
|
||||
"200":
|
||||
description: Validation report
|
||||
headers: { Idempotency-Replayed: { $ref: "#/components/headers/IdempotencyReplayed" } }
|
||||
content: { application/json: { schema: { $ref: "#/components/schemas/ValidationReport" } } }
|
||||
content: { application/json: { schema: { $ref: "#/components/schemas/ValidationReportEnvelope" } } }
|
||||
"400": { $ref: "#/components/responses/MalformedRequest" }
|
||||
"401": { $ref: "#/components/responses/AuthenticationRequired" }
|
||||
"403": { $ref: "#/components/responses/AccessDenied" }
|
||||
@@ -264,7 +264,7 @@ paths:
|
||||
anonymous `/preview/{token}` 계약은 폐기되었다. Preview는 인증된
|
||||
Studio API로만 조회한다.
|
||||
responses:
|
||||
"200": { description: Preview detail, content: { application/json: { schema: { $ref: "#/components/schemas/PreviewDetail" } } } }
|
||||
"200": { description: Preview detail, content: { application/json: { schema: { $ref: "#/components/schemas/PreviewDetailEnvelope" } } } }
|
||||
"401": { $ref: "#/components/responses/AuthenticationRequired" }
|
||||
"403": { $ref: "#/components/responses/AccessDenied" }
|
||||
"404": { $ref: "#/components/responses/PreviewNotFound" }
|
||||
@@ -285,7 +285,7 @@ paths:
|
||||
"201":
|
||||
description: Created preview
|
||||
headers: { Idempotency-Replayed: { $ref: "#/components/headers/IdempotencyReplayed" } }
|
||||
content: { application/json: { schema: { $ref: "#/components/schemas/PublicPreview" } } }
|
||||
content: { application/json: { schema: { $ref: "#/components/schemas/PublicPreviewEnvelope" } } }
|
||||
"400": { $ref: "#/components/responses/MalformedRequest" }
|
||||
"401": { $ref: "#/components/responses/AuthenticationRequired" }
|
||||
"403": { $ref: "#/components/responses/AccessDenied" }
|
||||
@@ -329,7 +329,7 @@ paths:
|
||||
"200":
|
||||
description: Publication aggregate and immutable event
|
||||
headers: { Idempotency-Replayed: { $ref: "#/components/headers/IdempotencyReplayed" } }
|
||||
content: { application/json: { schema: { $ref: "#/components/schemas/PublishResult" } } }
|
||||
content: { application/json: { schema: { $ref: "#/components/schemas/PublishResultEnvelope" } } }
|
||||
"400": { $ref: "#/components/responses/MalformedRequest" }
|
||||
"401": { $ref: "#/components/responses/AuthenticationRequired" }
|
||||
"403": { $ref: "#/components/responses/AccessDenied" }
|
||||
@@ -350,7 +350,7 @@ paths:
|
||||
- { $ref: "#/components/parameters/Cursor" }
|
||||
- { $ref: "#/components/parameters/Limit" }
|
||||
responses:
|
||||
"200": { description: Publication cursor page, content: { application/json: { schema: { $ref: "#/components/schemas/PublicationPage" } } } }
|
||||
"200": { description: Publication cursor page, content: { application/json: { schema: { $ref: "#/components/schemas/PublicationPageEnvelope" } } } }
|
||||
"400": { $ref: "#/components/responses/MalformedRequest" }
|
||||
"401": { $ref: "#/components/responses/AuthenticationRequired" }
|
||||
"403": { $ref: "#/components/responses/AccessDenied" }
|
||||
@@ -379,7 +379,7 @@ paths:
|
||||
"200":
|
||||
description: Updated publication aggregate and event
|
||||
headers: { Idempotency-Replayed: { $ref: "#/components/headers/IdempotencyReplayed" } }
|
||||
content: { application/json: { schema: { $ref: "#/components/schemas/PublishResult" } } }
|
||||
content: { application/json: { schema: { $ref: "#/components/schemas/PublishResultEnvelope" } } }
|
||||
"400": { $ref: "#/components/responses/MalformedRequest" }
|
||||
"401": { $ref: "#/components/responses/AuthenticationRequired" }
|
||||
"403": { $ref: "#/components/responses/AccessDenied" }
|
||||
@@ -401,7 +401,7 @@ paths:
|
||||
`UNPUBLISHED` Event는 자체 snapshot을 갖지 않는다. 이 경우
|
||||
`sourcePublishedEventId`가 가리키는 마지막 공개 Snapshot을 사용한다.
|
||||
responses:
|
||||
"200": { description: Publication snapshot, content: { application/json: { schema: { $ref: "#/components/schemas/PublicationSnapshot" } } } }
|
||||
"200": { description: Publication snapshot, content: { application/json: { schema: { $ref: "#/components/schemas/PublicationSnapshotEnvelope" } } } }
|
||||
"401": { $ref: "#/components/responses/AuthenticationRequired" }
|
||||
"403": { $ref: "#/components/responses/AccessDenied" }
|
||||
"404": { $ref: "#/components/responses/PublicationSnapshotNotFound" }
|
||||
@@ -430,7 +430,7 @@ paths:
|
||||
- { $ref: "#/components/parameters/Cursor" }
|
||||
- { $ref: "#/components/parameters/Limit" }
|
||||
responses:
|
||||
"200": { description: Catalog cursor page, content: { application/json: { schema: { $ref: "#/components/schemas/CatalogPage" } } } }
|
||||
"200": { description: Catalog cursor page, content: { application/json: { schema: { $ref: "#/components/schemas/CatalogPageEnvelope" } } } }
|
||||
"400": { $ref: "#/components/responses/MalformedRequest" }
|
||||
"401": { $ref: "#/components/responses/AuthenticationRequired" }
|
||||
"403": { $ref: "#/components/responses/AccessDenied" }
|
||||
@@ -449,7 +449,7 @@ paths:
|
||||
- { $ref: "#/components/parameters/Cursor" }
|
||||
- { $ref: "#/components/parameters/Limit" }
|
||||
responses:
|
||||
"200": { description: Asset cursor page, content: { application/json: { schema: { $ref: "#/components/schemas/AssetPage" } } } }
|
||||
"200": { description: Asset cursor page, content: { application/json: { schema: { $ref: "#/components/schemas/AssetPageEnvelope" } } } }
|
||||
"400": { $ref: "#/components/responses/MalformedRequest" }
|
||||
"401": { $ref: "#/components/responses/AuthenticationRequired" }
|
||||
"403": { $ref: "#/components/responses/AccessDenied" }
|
||||
@@ -483,7 +483,7 @@ paths:
|
||||
"201":
|
||||
description: Stored asset
|
||||
headers: { Idempotency-Replayed: { $ref: "#/components/headers/IdempotencyReplayed" } }
|
||||
content: { application/json: { schema: { $ref: "#/components/schemas/Asset" } } }
|
||||
content: { application/json: { schema: { $ref: "#/components/schemas/AssetEnvelope" } } }
|
||||
"400": { $ref: "#/components/responses/MalformedRequest" }
|
||||
"401": { $ref: "#/components/responses/AuthenticationRequired" }
|
||||
"403": { $ref: "#/components/responses/AccessDenied" }
|
||||
@@ -500,7 +500,7 @@ paths:
|
||||
tags: [Assets]
|
||||
summary: Get an asset with its usage
|
||||
responses:
|
||||
"200": { description: Asset detail, content: { application/json: { schema: { $ref: "#/components/schemas/AssetDetail" } } } }
|
||||
"200": { description: Asset detail, content: { application/json: { schema: { $ref: "#/components/schemas/AssetDetailEnvelope" } } } }
|
||||
"401": { $ref: "#/components/responses/AuthenticationRequired" }
|
||||
"403": { $ref: "#/components/responses/AccessDenied" }
|
||||
"404": { $ref: "#/components/responses/AssetNotFound" }
|
||||
@@ -520,7 +520,7 @@ paths:
|
||||
"200":
|
||||
description: Updated asset
|
||||
headers: { Idempotency-Replayed: { $ref: "#/components/headers/IdempotencyReplayed" } }
|
||||
content: { application/json: { schema: { $ref: "#/components/schemas/Asset" } } }
|
||||
content: { application/json: { schema: { $ref: "#/components/schemas/AssetEnvelope" } } }
|
||||
"400": { $ref: "#/components/responses/MalformedRequest" }
|
||||
"401": { $ref: "#/components/responses/AuthenticationRequired" }
|
||||
"403": { $ref: "#/components/responses/AccessDenied" }
|
||||
@@ -593,26 +593,26 @@ components:
|
||||
IdempotencyReplayed: { description: True when the original result was replayed, schema: { type: boolean } }
|
||||
|
||||
responses:
|
||||
MalformedRequest: { description: Malformed request, x-error-codes: [REQUEST_VALIDATION_FAILED], content: { application/problem+json: { schema: { $ref: "#/components/schemas/ProblemDetails" } } } }
|
||||
AuthenticationRequired: { description: Authentication required, x-error-codes: [AUTHENTICATION_REQUIRED], content: { application/problem+json: { schema: { $ref: "#/components/schemas/ProblemDetails" } } } }
|
||||
AccessDenied: { description: Studio access denied, x-error-codes: [STUDIO_ACCESS_DENIED], content: { application/problem+json: { schema: { $ref: "#/components/schemas/ProblemDetails" } } } }
|
||||
DocumentNotFound: { description: Document not found, x-error-codes: [DOCUMENT_NOT_FOUND], content: { application/problem+json: { schema: { $ref: "#/components/schemas/ProblemDetails" } } } }
|
||||
PreviewNotFound: { description: Document or preview not found, x-error-codes: [DOCUMENT_NOT_FOUND, PREVIEW_NOT_FOUND], content: { application/problem+json: { schema: { $ref: "#/components/schemas/ProblemDetails" } } } }
|
||||
PublicationNotFound: { description: Publication not found, x-error-codes: [PUBLICATION_NOT_FOUND], content: { application/problem+json: { schema: { $ref: "#/components/schemas/ProblemDetails" } } } }
|
||||
PublicationSnapshotNotFound: { description: Publication event or snapshot not found, x-error-codes: [PUBLICATION_EVENT_NOT_FOUND, PUBLICATION_SNAPSHOT_NOT_FOUND], content: { application/problem+json: { schema: { $ref: "#/components/schemas/ProblemDetails" } } } }
|
||||
AssetNotFound: { description: Asset not found, x-error-codes: [ASSET_NOT_FOUND], content: { application/problem+json: { schema: { $ref: "#/components/schemas/ProblemDetails" } } } }
|
||||
MalformedRequest: { description: Malformed request, x-error-codes: [REQUEST_VALIDATION_FAILED], content: { application/json: { schema: { $ref: "#/components/schemas/ErrorEnvelope" } } } }
|
||||
AuthenticationRequired: { description: Authentication required, x-error-codes: [AUTHENTICATION_REQUIRED], content: { application/json: { schema: { $ref: "#/components/schemas/ErrorEnvelope" } } } }
|
||||
AccessDenied: { description: Studio access denied, x-error-codes: [STUDIO_ACCESS_DENIED], content: { application/json: { schema: { $ref: "#/components/schemas/ErrorEnvelope" } } } }
|
||||
DocumentNotFound: { description: Document not found, x-error-codes: [DOCUMENT_NOT_FOUND], content: { application/json: { schema: { $ref: "#/components/schemas/ErrorEnvelope" } } } }
|
||||
PreviewNotFound: { description: Document or preview not found, x-error-codes: [DOCUMENT_NOT_FOUND, PREVIEW_NOT_FOUND], content: { application/json: { schema: { $ref: "#/components/schemas/ErrorEnvelope" } } } }
|
||||
PublicationNotFound: { description: Publication not found, x-error-codes: [PUBLICATION_NOT_FOUND], content: { application/json: { schema: { $ref: "#/components/schemas/ErrorEnvelope" } } } }
|
||||
PublicationSnapshotNotFound: { description: Publication event or snapshot not found, x-error-codes: [PUBLICATION_EVENT_NOT_FOUND, PUBLICATION_SNAPSHOT_NOT_FOUND], content: { application/json: { schema: { $ref: "#/components/schemas/ErrorEnvelope" } } } }
|
||||
AssetNotFound: { description: Asset not found, x-error-codes: [ASSET_NOT_FOUND], content: { application/json: { schema: { $ref: "#/components/schemas/ErrorEnvelope" } } } }
|
||||
CommandConflict:
|
||||
description: |
|
||||
Command conflicts with current state, freshness, or idempotency.
|
||||
|
||||
`ASSET_IN_USE`는 사용 중이거나 공개 이력이 있는 Asset의 hard delete 시도다.
|
||||
x-error-codes: [VERSION_CONFLICT, PUBLICATION_CONFLICT, VALIDATION_STALE, PREVIEW_STALE, PREVIEW_EXPIRED, IDEMPOTENCY_KEY_REUSED, ASSET_IN_USE]
|
||||
content: { application/problem+json: { schema: { $ref: "#/components/schemas/ProblemDetails" } } }
|
||||
RequestValidationFailed: { description: Request validation failed, x-error-codes: [REQUEST_VALIDATION_FAILED], content: { application/problem+json: { schema: { $ref: "#/components/schemas/ProblemDetails" } } } }
|
||||
content: { application/json: { schema: { $ref: "#/components/schemas/ErrorEnvelope" } } }
|
||||
RequestValidationFailed: { description: Request validation failed, x-error-codes: [REQUEST_VALIDATION_FAILED], content: { application/json: { schema: { $ref: "#/components/schemas/ErrorEnvelope" } } } }
|
||||
PreviewRejected:
|
||||
description: Preview 생성이 도메인 규칙으로 거절되었다
|
||||
x-error-codes: [REQUEST_VALIDATION_FAILED, VALIDATION_FAILED, ASSET_NOT_READY, ASSET_QUARANTINED]
|
||||
content: { application/problem+json: { schema: { $ref: "#/components/schemas/ProblemDetails" } } }
|
||||
content: { application/json: { schema: { $ref: "#/components/schemas/ErrorEnvelope" } } }
|
||||
PublishRejected:
|
||||
description: |
|
||||
Publication validation이 실패했다.
|
||||
@@ -620,16 +620,205 @@ components:
|
||||
`WARNING_ACKNOWLEDGEMENT_REQUIRED`는 `acknowledgedWarningCodes`가
|
||||
현재 Validation의 WARNING 집합을 덮지 못한 경우다.
|
||||
x-error-codes: [REQUEST_VALIDATION_FAILED, VALIDATION_FAILED, WARNING_ACKNOWLEDGEMENT_REQUIRED, ASSET_NOT_READY, ASSET_QUARANTINED]
|
||||
content: { application/problem+json: { schema: { $ref: "#/components/schemas/ProblemDetails" } } }
|
||||
content: { application/json: { schema: { $ref: "#/components/schemas/ErrorEnvelope" } } }
|
||||
AssetRejected:
|
||||
description: Asset metadata 변경이 거절되었다
|
||||
x-error-codes: [REQUEST_VALIDATION_FAILED, ASSET_NOT_READY, ASSET_QUARANTINED]
|
||||
content: { application/problem+json: { schema: { $ref: "#/components/schemas/ProblemDetails" } } }
|
||||
PayloadTooLarge: { description: Upload exceeds the configured size limit, x-error-codes: [PAYLOAD_TOO_LARGE], content: { application/problem+json: { schema: { $ref: "#/components/schemas/ProblemDetails" } } } }
|
||||
UnsupportedMediaType: { description: Unsupported media type, x-error-codes: [UNSUPPORTED_MEDIA_TYPE], content: { application/problem+json: { schema: { $ref: "#/components/schemas/ProblemDetails" } } } }
|
||||
StudioUnavailable: { description: Studio unavailable, x-error-codes: [STUDIO_UNAVAILABLE], content: { application/problem+json: { schema: { $ref: "#/components/schemas/ProblemDetails" } } } }
|
||||
content: { application/json: { schema: { $ref: "#/components/schemas/ErrorEnvelope" } } }
|
||||
PayloadTooLarge: { description: Upload exceeds the configured size limit, x-error-codes: [PAYLOAD_TOO_LARGE], content: { application/json: { schema: { $ref: "#/components/schemas/ErrorEnvelope" } } } }
|
||||
UnsupportedMediaType: { description: Unsupported media type, x-error-codes: [UNSUPPORTED_MEDIA_TYPE], content: { application/json: { schema: { $ref: "#/components/schemas/ErrorEnvelope" } } } }
|
||||
StudioUnavailable: { description: Studio unavailable, x-error-codes: [STUDIO_UNAVAILABLE], content: { application/json: { schema: { $ref: "#/components/schemas/ErrorEnvelope" } } } }
|
||||
|
||||
schemas:
|
||||
# ------------------------------------------------------------- envelope
|
||||
# wire format은 봉투다 (ADR-006). payload 스키마는 그대로 두고
|
||||
# 응답만 <Payload>Envelope으로 감싼다.
|
||||
ResponseMeta:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [requestId, traceId]
|
||||
properties:
|
||||
requestId: { type: string, minLength: 1, maxLength: 200 }
|
||||
traceId: { type: string, minLength: 1, maxLength: 200 }
|
||||
correlationId: { type: [string, "null"], maxLength: 200 }
|
||||
page: { type: "null", description: Studio는 body 안 cursor 페이지네이션을 쓰므로 항상 null이다 }
|
||||
ApiError:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [code, category, message, retryable]
|
||||
properties:
|
||||
code:
|
||||
type: string
|
||||
enum: [AUTHENTICATION_REQUIRED, STUDIO_ACCESS_DENIED, DOCUMENT_NOT_FOUND, VERSION_CONFLICT,
|
||||
REQUEST_VALIDATION_FAILED, VALIDATION_FAILED, VALIDATION_STALE, PREVIEW_NOT_FOUND,
|
||||
PREVIEW_STALE, PREVIEW_EXPIRED, PUBLICATION_NOT_FOUND, PUBLICATION_CONFLICT,
|
||||
PUBLICATION_EVENT_NOT_FOUND, PUBLICATION_SNAPSHOT_NOT_FOUND,
|
||||
WARNING_ACKNOWLEDGEMENT_REQUIRED, IDEMPOTENCY_KEY_REUSED, ASSET_NOT_FOUND,
|
||||
ASSET_NOT_READY, ASSET_IN_USE, ASSET_QUARANTINED, PAYLOAD_TOO_LARGE,
|
||||
UNSUPPORTED_MEDIA_TYPE, STUDIO_UNAVAILABLE]
|
||||
category:
|
||||
type: string
|
||||
enum: [VALIDATION, AUTH, AUTHZ, NOT_FOUND, CONFLICT, RATE_LIMIT,
|
||||
TRANSIENT_DEPENDENCY, PERMANENT_DEPENDENCY, DATA_INTEGRITY, INTERNAL]
|
||||
message: { type: string, minLength: 1, maxLength: 5000 }
|
||||
retryable: { type: boolean }
|
||||
details:
|
||||
oneOf:
|
||||
- $ref: "#/components/schemas/ValidationErrorDetails"
|
||||
- $ref: "#/components/schemas/VersionConflictDetails"
|
||||
- $ref: "#/components/schemas/PublicationConflictDetails"
|
||||
- type: "null"
|
||||
ErrorEnvelope:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [success, error, meta]
|
||||
properties:
|
||||
success: { type: boolean, const: false }
|
||||
error: { $ref: "#/components/schemas/ApiError" }
|
||||
meta: { $ref: "#/components/schemas/ResponseMeta" }
|
||||
ValidationErrorDetails:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [fieldErrors]
|
||||
properties:
|
||||
fieldErrors: { type: array, maxItems: 200, items: { $ref: "#/components/schemas/FieldError" } }
|
||||
VersionConflictDetails:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [latestDocument]
|
||||
properties:
|
||||
latestDocument: { $ref: "#/components/schemas/WorkingCopyDetail" }
|
||||
conflictingFields:
|
||||
type: array
|
||||
uniqueItems: true
|
||||
maxItems: 200
|
||||
items: { type: string, pattern: "^(?:/(?:[^~/]|~0|~1)*)*$" }
|
||||
PublicationConflictDetails:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [latestPublication]
|
||||
properties:
|
||||
latestPublication: { $ref: "#/components/schemas/PublicationAggregate" }
|
||||
StudioSessionEnvelope:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [success, data, meta]
|
||||
properties:
|
||||
success: { type: boolean, const: true }
|
||||
data: { $ref: "#/components/schemas/StudioSession" }
|
||||
meta: { $ref: "#/components/schemas/ResponseMeta" }
|
||||
StudioDashboardEnvelope:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [success, data, meta]
|
||||
properties:
|
||||
success: { type: boolean, const: true }
|
||||
data: { $ref: "#/components/schemas/StudioDashboard" }
|
||||
meta: { $ref: "#/components/schemas/ResponseMeta" }
|
||||
DocumentPageEnvelope:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [success, data, meta]
|
||||
properties:
|
||||
success: { type: boolean, const: true }
|
||||
data: { $ref: "#/components/schemas/DocumentPage" }
|
||||
meta: { $ref: "#/components/schemas/ResponseMeta" }
|
||||
WorkingCopyDetailEnvelope:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [success, data, meta]
|
||||
properties:
|
||||
success: { type: boolean, const: true }
|
||||
data: { $ref: "#/components/schemas/WorkingCopyDetail" }
|
||||
meta: { $ref: "#/components/schemas/ResponseMeta" }
|
||||
WorkingCopyEnvelope:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [success, data, meta]
|
||||
properties:
|
||||
success: { type: boolean, const: true }
|
||||
data: { $ref: "#/components/schemas/WorkingCopy" }
|
||||
meta: { $ref: "#/components/schemas/ResponseMeta" }
|
||||
ValidationReportEnvelope:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [success, data, meta]
|
||||
properties:
|
||||
success: { type: boolean, const: true }
|
||||
data: { $ref: "#/components/schemas/ValidationReport" }
|
||||
meta: { $ref: "#/components/schemas/ResponseMeta" }
|
||||
PreviewDetailEnvelope:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [success, data, meta]
|
||||
properties:
|
||||
success: { type: boolean, const: true }
|
||||
data: { $ref: "#/components/schemas/PreviewDetail" }
|
||||
meta: { $ref: "#/components/schemas/ResponseMeta" }
|
||||
PublicPreviewEnvelope:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [success, data, meta]
|
||||
properties:
|
||||
success: { type: boolean, const: true }
|
||||
data: { $ref: "#/components/schemas/PublicPreview" }
|
||||
meta: { $ref: "#/components/schemas/ResponseMeta" }
|
||||
PublishResultEnvelope:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [success, data, meta]
|
||||
properties:
|
||||
success: { type: boolean, const: true }
|
||||
data: { $ref: "#/components/schemas/PublishResult" }
|
||||
meta: { $ref: "#/components/schemas/ResponseMeta" }
|
||||
PublicationPageEnvelope:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [success, data, meta]
|
||||
properties:
|
||||
success: { type: boolean, const: true }
|
||||
data: { $ref: "#/components/schemas/PublicationPage" }
|
||||
meta: { $ref: "#/components/schemas/ResponseMeta" }
|
||||
PublicationSnapshotEnvelope:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [success, data, meta]
|
||||
properties:
|
||||
success: { type: boolean, const: true }
|
||||
data: { $ref: "#/components/schemas/PublicationSnapshot" }
|
||||
meta: { $ref: "#/components/schemas/ResponseMeta" }
|
||||
CatalogPageEnvelope:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [success, data, meta]
|
||||
properties:
|
||||
success: { type: boolean, const: true }
|
||||
data: { $ref: "#/components/schemas/CatalogPage" }
|
||||
meta: { $ref: "#/components/schemas/ResponseMeta" }
|
||||
AssetPageEnvelope:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [success, data, meta]
|
||||
properties:
|
||||
success: { type: boolean, const: true }
|
||||
data: { $ref: "#/components/schemas/AssetPage" }
|
||||
meta: { $ref: "#/components/schemas/ResponseMeta" }
|
||||
AssetDetailEnvelope:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [success, data, meta]
|
||||
properties:
|
||||
success: { type: boolean, const: true }
|
||||
data: { $ref: "#/components/schemas/AssetDetail" }
|
||||
meta: { $ref: "#/components/schemas/ResponseMeta" }
|
||||
AssetEnvelope:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [success, data, meta]
|
||||
properties:
|
||||
success: { type: boolean, const: true }
|
||||
data: { $ref: "#/components/schemas/Asset" }
|
||||
meta: { $ref: "#/components/schemas/ResponseMeta" }
|
||||
# ---------------------------------------------------------------- session
|
||||
StudioSession:
|
||||
type: object
|
||||
@@ -1645,51 +1834,3 @@ components:
|
||||
pattern: "^(?:/(?:[^~/]|~0|~1)*)*$"
|
||||
description: JSON Pointer to the invalid field
|
||||
message: { type: string, minLength: 1, maxLength: 1000 }
|
||||
ProblemDetails:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
required: [type, title, status, detail, code]
|
||||
properties:
|
||||
type: { type: string, format: uri-reference }
|
||||
title: { type: string, minLength: 1, maxLength: 200 }
|
||||
status: { type: integer, minimum: 400, maximum: 599 }
|
||||
detail: { type: string, minLength: 1, maxLength: 5000 }
|
||||
code:
|
||||
type: string
|
||||
enum:
|
||||
- AUTHENTICATION_REQUIRED
|
||||
- STUDIO_ACCESS_DENIED
|
||||
- DOCUMENT_NOT_FOUND
|
||||
- VERSION_CONFLICT
|
||||
- REQUEST_VALIDATION_FAILED
|
||||
- VALIDATION_FAILED
|
||||
- VALIDATION_STALE
|
||||
- PREVIEW_NOT_FOUND
|
||||
- PREVIEW_STALE
|
||||
- PREVIEW_EXPIRED
|
||||
- PUBLICATION_NOT_FOUND
|
||||
- PUBLICATION_CONFLICT
|
||||
- PUBLICATION_EVENT_NOT_FOUND
|
||||
- PUBLICATION_SNAPSHOT_NOT_FOUND
|
||||
- WARNING_ACKNOWLEDGEMENT_REQUIRED
|
||||
- IDEMPOTENCY_KEY_REUSED
|
||||
- ASSET_NOT_FOUND
|
||||
- ASSET_NOT_READY
|
||||
- ASSET_IN_USE
|
||||
- ASSET_QUARANTINED
|
||||
- PAYLOAD_TOO_LARGE
|
||||
- UNSUPPORTED_MEDIA_TYPE
|
||||
- STUDIO_UNAVAILABLE
|
||||
instance: { type: string, format: uri-reference }
|
||||
traceId: { type: string, maxLength: 200 }
|
||||
fieldErrors: { type: array, maxItems: 200, items: { $ref: "#/components/schemas/FieldError" } }
|
||||
latestDocument: { $ref: "#/components/schemas/WorkingCopyDetail" }
|
||||
latestPublication: { $ref: "#/components/schemas/PublicationAggregate" }
|
||||
conflictingFields:
|
||||
type: array
|
||||
uniqueItems: true
|
||||
maxItems: 200
|
||||
items:
|
||||
type: string
|
||||
pattern: "^(?:/(?:[^~/]|~0|~1)*)*$"
|
||||
retryable: { type: boolean }
|
||||
|
||||
Reference in New Issue
Block a user