feat(exporter): Prisma ORM exporter 추가 (PR #151 이어받음)#166
Open
yyuneu wants to merge 7 commits into
Open
Conversation
Add a Prisma schema generator as a fourth ORM backend alongside SeaORM, SQLAlchemy, and SQLModel. Renders enum + model blocks with full schema context (relations, indexes, unique/composite constraints, native types, default attributes, referential actions) and wires Prisma into the cross-ORM test harness and CLI export command. Model names use plural PascalCase; string default values escape backslashes correctly. Snapshots cover the full column-type matrix plus relation, enum, default, and reserved-identifier scenarios. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Generate the 60 missing Prisma insta snapshots so the Prisma backend is cross-compared with the other four ORMs through the shared orm_cases! matrix. The Orm::Prisma cases were already wired into every test; only the accepted snapshot files were absent. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mirrors the sqlmodel/sqlalchemy 4-file convention. mod.rs is now a thin orchestrator; render.rs holds model rendering and relation logic; types.rs holds column-type mapping; enums.rs holds enum rendering and naming helpers. Public API surface (PrismaExporter, PrismaExporterWithConfig, export, render_entity, render_entity_with_schema, to_pascal_case_for_tests) unchanged. All 565 tests pass with byte-identical snapshot output. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…, relation field naming - @@unique/@@index DB constraint names now emit `map:` instead of `name:` (`name:` on @@index is invalid in Prisma 2.30+; on @@unique it sets the Prisma Client accessor, not the DB constraint name) - integer-backed enum @default(...) now resolves to a variant identifier via numeric-value or variant-name match (e.g. @default(COMPLETED)), falling back to dbgenerated() when unmatched, instead of emitting an invalid bare int - inline FK relation field gets a `_rel` suffix when stripping `_id` would collide with the scalar column; self-referential back-relation is emitted - regenerate affected Prisma snapshots Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…f a struct literal
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
개요
5번째 ORM 백엔드로 Prisma를 추가합니다 (기존: SeaORM / SQLAlchemy / SQLModel / JPA).
TableDef스키마를 datasource, generator, enum 블록(전역 dedup), model 블록을포함한 단일
schema.prisma파일로 변환합니다.실행:
vespertide export --orm prisma설계상 특이사항
1. 왜 Prisma만 별도 config (
PrismaConfig)를 가지는가Prisma는 단일 스키마 언어입니다.
datasource db { provider, url, relationMode }와generator client { provider, output }이 모델과 같은 파일 안에 있어야 합니다.나머지 ORM 백엔드(SeaORM / SQLAlchemy / SQLModel / JPA)는 모델 단위 코드만
생성하므로 이런 메타 정보가 필요 없습니다. 즉, Prisma만 "모델 외 입력"(provider,
client output 경로, relation mode)을 받아야 하기 때문에
vespertide-config에PrismaConfig를 새로 추가했습니다. 필드 세 개(provider / client_output /relation_mode)와 postgresql 기본값을 가집니다.
2. 왜
export.rs에if matches!(orm, OrmArg::Prisma) { return cmd_export_prisma(...) }분기가 있는가기존 export 파이프라인은 모델 N개 → 파일 N개 전제입니다:
walk_models→ 모델별render_entity_with_schema→ 파일별 write → mod chain.Prisma는 모델 N개 → 파일 1개이며 위 파이프라인이 만족시킬 수 없는 두 가지
요구가 있습니다:
블록은 파일에 한 번만 등장해야 함)
그래서
cmd_export가 진입 즉시cmd_export_prisma로 분기하고,PrismaExporterWithConfig::render_schema(&all_tables)에서 스키마 전체를 한 번에조립합니다.
Orm::Prisma는 인터페이스 일관성 + 분기 안 자체 clean 호출을 위해clean_export_dir/build_output_path에prisma확장자로 함께 등록만 해두었습니다.
테스트 설계
명세는
crates/vespertide-exporter/src/prisma/TESTING.md에 있습니다. 3-layer 구조:render_entity(table)로 검증.render_entity_with_schema(table, schema)로 검증.TableConstraint::ForeignKey가하나라도 있는
TableDef는 무조건 Layer 2.설계 규칙:
insta::assert_snapshot!사용.assert!(result.contains(...))방식은 금지 — 회귀 감지 정확도 확보 및 출력 전체를 고정.
singularize 등)는
#[rstest]파라미터화로 작성.ColumnDef생성은 로컬 헬퍼(col,col_null,col_with_default,col_with_comment,pk,uniq,idx,fk,table,render_schema_all)로분리하여 케이스 본문을 짧게 유지.
테스트 65개 / 스냅샷 54개가 커버하는 범위:
select,order,model, ...), 예약어 컬럼명 (default,unique, ...), description 안의 newline/quote/brace, default 값 안의 quote이 PR에 대해 (원본 #151 이어받음)
원본 PR은 #151, @L33gn21 님이 작성하셨습니다. 다만 해당 fork에 제가 쓰기 권한이 없고, 작성자분이 미국에 계셔서 시차 때문에 실시간으로 논의하기 어려운 상황이라, 같은 브랜치를 제 fork(
yyuneu/vespertide)로 이어받아 새 PR로 올립니다. 원본 커밋 4개는 그대로 두었고, 리뷰 반영 커밋 3개만 위에 추가했습니다.PrismaConfig리뷰 피드백 반영@owjs3901 님이 남겨주신 코멘트:
세 가지 다 맞는 지적이라 생각해서 그대로 반영했습니다.
clientOutput:
prisma generate가 Client 라이브러리를 어디에 생성할지 정하는 값이라modelsDir(모델 소스 위치)이랑 정확히 같은 개념은 아니지만, vespertide의 책임 범위(스키마/모델 정의) 밖이라는 말씀이 맞다고 판단해 필드를 제거했습니다.provider: 지금 exporter는 PostgreSQL 네이티브 타입(
@db.Uuid등)만 생성하는데, config로 mysql/sqlite를 선택할 수 있게 열어두면 실제로는 깨진 스키마가 나오면서도 마치 지원하는 것처럼 보이는 문제가 있었습니다. 그래서 옵션은 제거하고provider = "postgresql"을 고정값으로 넣었습니다. (자세한 이유는 아래 참고 부탁드립니다.)relationMode:
RelationModeenum(ForeignKeys/Prisma)을 추가했습니다.foreignKeys는 실제 DB FK 제약을 쓰는 기본값이고,prisma는 PlanetScale처럼 FK를 지원하지 않는 DB에서 Prisma Client가 대신 참조무결성을 챙겨주는 모드입니다.이렇게 정리되면서
PrismaConfig는 필드가relation_mode하나만 남았습니다.schemas/config.schema.json도 다시 생성해서 반영했습니다.참고: provider를 postgres 전용으로 남겨둔 이유
Prisma는 다른 4개 ORM(SeaORM 등)과 달리, 스키마 파일 자체에 DB 종류를 못박아야 하는 구조라(
datasource.provider), 코드 생성 시점에 dialect를 미리 알아야 합니다.MySQL: 네이티브 타입 매핑만 추가하면 되는 작업이라 나중에 추가가 가능하다 생각합니다.
SQLite: vespertide 모델에서 컬럼 타입을 enum으로 정의합니다. 하지만 Prisma의 sqlite 커넥터가
enum자체를 공식 문서에서 지원하지 않아서, 단순 타입 매핑으로는 안 되고 enum을 문자열로 강등하는 등 별도 설계가 필요합니다.원본 PR도 처음부터 postgres 위주로 만들어져 있었고, 이번 리뷰에서도 멀티 dialect 자체는 별도 지적이 없으셔서, 그 방향 그대로 이번 PR도 postgres 전용으로 유지하고 mysql/sqlite는 후속 작업으로 남깁니다.
추가로 확인해서 함께 반영한 부분: 스냅샷 4건
변경 사항을 검증하면서 exporter 테스트 전체를 돌려봤는데, 이번 변경과는 무관한 기존 실패 4건을 발견했습니다. 확인해보니 원본의
dcbff76커밋(제약명name:→map:, 정수 enum 기본값 처리, 숫자로 시작하는 식별자 처리)에서 코드 수정 자체는 정확히 반영되어 있었는데, 그에 맞춰 갱신됐어야 할 스냅샷 4개가 아직 반영되어 있지 않았던 것으로 보였습니다. 테스트 코드에 정의된 값과 비교해서 현재 코드 출력이 의도한 대로 맞다는 것도 확인했고, 코드 변경 없이 스냅샷만 최신 상태로 갱신했습니다.Test plan
cargo build -p vespertide-config -p vespertide-exporter -p vespertide-clicargo test -p vespertide-config -p vespertide-exporter -p vespertide-cli— 전부 통과cargo test -p vespertide-exporter— 567 passed, 0 failedcargo run -p vespertide-schema-gen -- --out schemas재생성 후 diff 확인 —PrismaConfig관련 의도된 변경만 존재vespertide export --orm prisma실행해서 실제schema.prisma확인 (provider 고정, clientOutput/relationMode 줄 없음,@@Map 유지)
괜찮으실지 확인 부탁드립니다.