docs(contract): declare the provider key header, the shared 429 and 503, and the request id - #1024
Conversation
|
머지 순서 주의. 이 PR 이 추가하는 에러 예시와 |
The fixture released the first service's worker and returned. The worker then ran its build behind the following tests, and test_json_array_reader measures peak memory with tracemalloc, which counts every thread. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…000-stable-error-codes
The code was read back from the end of the reason sentence, so rewording the reason would have turned token_expired into unauthorized without a failure anywhere. AuthError now carries the fact. Also pin that a missing API key and a wrong one both answer with the contract example's sentence. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-contract-headers-and-shared-errors
…pted-run-status # Conflicts: # CHANGELOG.md
…000-stable-error-codes # Conflicts: # tests/unit/test_oidc_auth.py
…-contract-headers-and-shared-errors # Conflicts: # CHANGELOG.md
…tract (#743) Refs #727 ## 이 PR 이 하는 것 — 완료 조건 3번 "클라이언트의 모든 method+path 가 계약에 있는지 단언한다." `builderApi` 의 59개 함수 각각을 자리표시 인자로 호출하고(stub `fetch`), 보낸 method 와 path 가 계약의 operation 과 맞는지 본다. path 파라미터는 세그먼트 하나와 맞는다. - 소스 정적 분석이 아니라 **실제 호출**을 기록한다 — 템플릿 문자열·조건부 경로를 그대로 따라간다. - probe 가 요청까지 끌고 가지 못하는 함수는 `UNPROBED` 목록에 사유와 함께 올려야 한다. 지금은 빈 목록이다(59개 전부 요청을 보냈다). 새 함수가 조용히 빠지지 않는다. - CI 가 `BUILDER_CONTRACT` 를 주고 실행하는 파일이 `src/shared/lib/contractDrift.test.ts` 하나라서, 별도 파일이 아니라 그 파일 끝에 넣었다. 계약이 없으면 다른 드리프트 검사처럼 skip 된다. ## 검증 - Builder 계약(1.78.0, kpubdata-lab/kpubdata-builder#996 브랜치 — main 의 1.77.0 과 경로는 같다) 기준: `553 passed`. 계약 없이: 기존대로 skip. - 매처 자체의 테스트: 선언된 경로는 통과, 선언되지 않은 method(`DELETE /builds/{id}/manifest`)·없는 경로·세그먼트가 하나 더 많은 경로는 거부. - `tsc --noEmit`, eslint 통과. ## 추가 (두 번째 커밋) — 완료 조건 2번과 Builder 의 에러 코드 - **`X-Provider-Key`**: probe 가 이 헤더를 싣는 호출 다섯(`build`, `preview`, `submitBuild`, `getProviderStatus`, `testProviderConnection`)을 찾고, 계약이 그 operation 에 파라미터로 선언했는지 본다. 계약 1.80.0(kpubdata-lab/kpubdata-builder#1024) 부터 강제하고, 그 전 계약에서는 다섯 개를 찾았는지만 확인한다. - **이 PR 은 Builder 의 #1023·#1024 보다 먼저 머지되어야 한다.** 그 두 PR 이 추가하는 에러 예시(`build_queue_full`, `auth_throttled`, `server_overloaded`)와 401 예시의 `code: unauthorized` 를 지금의 Studio 드리프트 검사는 모른다 — Builder `main` 에 들어가는 순간 Studio 의 모든 PR 에서 `Builder contract drift` 가 실패한다. 직접 돌려 확인했다(7개 실패). - 그래서 `ERROR_READERS` 항목에 `since`(그 예시나 코드가 생긴 계약 버전)를 둘 수 있게 했다. 그 버전 전의 계약에서는 예시가 없어도 stale 로 보지 않고, 있어도 code 가 없는 것을 정상으로 본다. Studio CI 가 Builder `main` 을 읽는 구조에서 양쪽 PR 이 서로를 깨뜨리지 않고 순서대로 들어갈 수 있게 하는 장치다. 드리프트 테스트를 세 계약에 대해 돌린 결과: Builder `main`(1.77.0) `555 passed`, #1022(1.78.0) `555 passed`, #1024 까지(1.80.0) `558 passed`. ## 남은 것 없다 — #727 의 세 조건을 모두 다뤘다. 다만 2번은 Builder #1024 가 머지돼야 실제로 강제되므로 `Refs` 로 둔다. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Eomdahyeon <213566566+Eomdahyeon@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
yeongseon
left a comment
There was a problem hiding this comment.
리뷰 (코멘트 — #1023 위에 쌓인 PR 이고 체크가 도는 중이라 승인은 그 뒤에).
diff 를 읽었고 걸리는 점이 없습니다. 선언마다 실제 응답과 대조하는 테스트가 있고(_overloaded_response() 의 상태·본문·헤더, 한도를 넘긴 실제 429, 실제 HTTP 응답의 X-Request-ID), ProviderKey 를 참조하는 operation 집합을 정확히 다섯으로 고정했습니다. X-Request-ID 를 응답마다 달지 않고 헤더 컴포넌트로만 둔 판단도 동의합니다.
본문이 물은 순서 — 이 스택을 먼저 넣습니다. #1022 → (studio#743) → #1023 → #1024 순으로 1.78.0 ~ 1.80.0 을 쓰고, #1009 는 그 뒤에 리베이스해서 다음 번호(1.81.0)를 씁니다. #1009 는 어차피 지금 충돌 상태라 다시 손봐야 하고, 이 스택은 그대로 들어갈 수 있습니다. 이쪽 버전 줄은 바꿀 필요 없습니다.
로컬에서 테스트를 돌리지는 않았습니다.
…e-error-codes # Conflicts: # contract/builder-api.yaml # contract/fixtures/responses.json # src/kpubdata_builder/service/app.py
…-contract-headers-and-shared-errors
…ct-headers-and-shared-errors # Conflicts: # contract/builder-api.yaml # contract/fixtures/responses.json # src/kpubdata_builder/service/app.py
Refs #994
이 PR 이 하는 것 — #1009 가 "이 PR 에 없는 것"으로 남긴 세 항목 중 둘
X-Provider-Key를 header 파라미터로 선언 —components.parameters.ProviderKey. 프로바이더를 호출하는 다섯 operation(previewBuild,createBuild,submitBuild,getProviderStatus,testProviderConnection)에 참조를 달았다. Studio 가 이 헤더를 보내는 호출과 같은 다섯이다.auth_throttled, 과부하 503,X-Request-ID— 어느 operation 에서나 나올 수 있으므로SignupNotApproved와 같은 방식(x-status를 가진 공용 응답)으로 선언했다:components.responses.AuthThrottled,ServerOverloaded, 그리고components.headers.RequestId.계약 1.79.0 → 1.80.0. wire 는 바뀌지 않는다(선언만). response fixture 를 생성기로 다시 만들었다.
하지 않은 것
_DISPATCH_ROUTES를 라우트 어댑터에서 생성 — docs(contract): declare the publish recovery routes #1009 본문이 적은 대로 라우트가 if 연쇄라 표로 바꾸는 리팩터링이 먼저다. 이 PR 범위에 넣지 않았다. 그래서Refs.X-Request-ID는 헤더 컴포넌트로만 선언했고 각 operation 의 응답마다 참조를 달지는 않았다 — 모든 응답(수백 개)에 다는 것은 계약을 읽기 어렵게 만든다고 판단했다. 설명에 "핸들러가 쓰는 모든 응답에 실린다"고 적었다.검증 — 선언과 실제를 대조한다
tests/unit/test_contract_shared_declarations.py:PROVIDER_KEY_HEADER와 같고, 참조한 operation 집합이 정확히 위 다섯이다_overloaded_response()의 상태·본문·헤더가ServerOverloaded선언과 같다code가AuthThrottled선언과 같다X-Request-ID가 있다계약 관련 테스트 파일:
424 passed.check_contract_compat.py:origin/main대비1.77.0 -> 1.80.0호환. ruff, mypy 통과. 전체 스위트는 CI 가 돌린다.#1009 와의 관계
#1009 는 지금
main과 충돌 상태다 — 내가 #1010 을 머지하면서main이 1.77.0 이 됐고, #1009 도 1.77.0 을 쓴다. 이 스택(#1022 → #1023 → 이 PR)은 1.78.0 ~ 1.80.0 을 쓴다. 어느 쪽이 먼저 들어가든 나중 쪽이 버전 줄을 다시 맞춰야 하니, 순서를 정해 주면 내 쪽을 그에 맞추겠다.🤖 Generated with Claude Code