Skip to content

Commit 5dc7e9a

Browse files
committed
fix(cli): fixes field casing and sort issues
1 parent a8b7e71 commit 5dc7e9a

File tree

3 files changed

+123
-21
lines changed

3 files changed

+123
-21
lines changed

packages/cli/src/actions/pull/index.ts

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,8 @@ export function syncTable({
378378
}
379379
arrayExpr.addItem((itemBuilder) => {
380380
const refExpr = itemBuilder.ReferenceExpr.setTarget(ref);
381-
if (c.order !== 'ASC') refExpr.addArg((ab) => ab.StringLiteral.setValue('DESC'), 'sort');
381+
if (c.order && c.order !== 'ASC')
382+
refExpr.addArg((ab) => ab.StringLiteral.setValue('DESC'), 'sort');
382383

383384
return refExpr;
384385
});
@@ -402,6 +403,7 @@ export function syncRelation({
402403
model,
403404
relation,
404405
services,
406+
options,
405407
selfRelation,
406408
simmilarRelations,
407409
}: {
@@ -444,10 +446,12 @@ export function syncRelation({
444446
const fieldPrefix = /[0-9]/g.test(sourceModel.name.charAt(0)) ? '_' : '';
445447

446448
const relationName = `${relation.table}${simmilarRelations > 1 ? `_${relation.column}` : ''}To${relation.references.table}`;
447-
let sourceFieldName =
449+
let { name: sourceFieldName } = resolveNameCasing(
450+
options.fieldCasing,
448451
simmilarRelations > 0
449452
? `${fieldPrefix}${sourceModel.name.charAt(0).toLowerCase()}${sourceModel.name.slice(1)}_${relation.column}`
450-
: targetModel.name;
453+
: targetModel.name,
454+
);
451455

452456
if (sourceModel.fields.find((f) => f.name === sourceFieldName)) {
453457
sourceFieldName = `${sourceFieldName}To${targetModel.name.charAt(0).toLowerCase()}${targetModel.name.slice(1)}_${relation.references.column}`;
@@ -498,10 +502,12 @@ export function syncRelation({
498502
sourceModel.fields.push(sourceFieldFactory.node);
499503

500504
const oppositeFieldPrefix = /[0-9]/g.test(targetModel.name.charAt(0)) ? '_' : '';
501-
const oppositeFieldName =
505+
const { name: oppositeFieldName } = resolveNameCasing(
506+
options.fieldCasing,
502507
simmilarRelations > 0
503508
? `${oppositeFieldPrefix}${sourceModel.name.charAt(0).toLowerCase()}${sourceModel.name.slice(1)}_${relation.column}`
504-
: sourceModel.name;
509+
: sourceModel.name,
510+
);
505511

506512
const targetFieldFactory = new DataFieldFactory()
507513
.setContainer(targetModel)
@@ -519,8 +525,8 @@ export function syncRelation({
519525

520526
targetModel.fields.push(targetFieldFactory.node);
521527

522-
targetModel.fields.sort((a, b) => {
523-
if (a.type.reference && b.type.reference) return 0;
524-
return a.name.localeCompare(b.name);
525-
});
528+
// targetModel.fields.sort((a, b) => {
529+
// if (a.type.reference || b.type.reference) return a.name.localeCompare(b.name);
530+
// return 0;
531+
// });
526532
}

packages/cli/test/db/pull.test.ts

Lines changed: 96 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
import fs from 'node:fs';
22
import path from 'node:path';
33
import { describe, expect, it } from 'vitest';
4-
import { createProject, runCli } from '../utils';
4+
import { createProject, getDefaultPrelude, runCli } from '../utils';
5+
import { loadSchemaDocument } from '../../src/actions/action-utils';
6+
import { ZModelCodeGenerator } from '@zenstackhq/language';
57

68
const getSchema = (workDir: string) => fs.readFileSync(path.join(workDir, 'zenstack/schema.zmodel')).toString();
9+
const generator = new ZModelCodeGenerator({
10+
quote: 'double',
11+
indent: 4,
12+
});
713

814
describe('DB pull', () => {
9-
it('simple schema', () => {
15+
it("simple schema - pull shouldn't modify the schema", () => {
1016
const workDir = createProject(
11-
`model User {
17+
`model User {
1218
id String @id @default(cuid())
1319
email String @unique @map("email_address")
1420
name String? @default("Anonymous")
@@ -85,12 +91,98 @@ enum Role {
8591
USER
8692
ADMIN
8793
MODERATOR
88-
}`);
94+
}`,
95+
);
8996
runCli('format', workDir);
9097
runCli('db push', workDir);
9198

9299
const originalSchema = getSchema(workDir);
93100
runCli('db pull --indent 4', workDir);
94101
expect(getSchema(workDir)).toEqual(originalSchema);
95102
});
103+
104+
it('simple schema - pull shouldn recreate the schema.zmodel', async () => {
105+
const workDir = createProject(
106+
`model Post {
107+
id Int @id @default(autoincrement())
108+
authorId String
109+
title String
110+
content String?
111+
published Boolean @default(false)
112+
createdAt DateTime @default(now())
113+
updatedAt DateTime @updatedAt
114+
slug String
115+
score Float @default(0.0)
116+
metadata Json?
117+
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
118+
PostTag PostTag[]
119+
120+
@@unique([authorId, slug])
121+
@@index([authorId, published])
122+
}
123+
model PostTag {
124+
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
125+
postId Int
126+
tag Tag @relation(fields: [tagId], references: [id], onDelete: Cascade)
127+
tagId Int
128+
assignedAt DateTime @default(now())
129+
note String? @default("initial")
130+
131+
@@id([postId, tagId])
132+
}
133+
model User {
134+
id String @id @default(cuid())
135+
email String @unique
136+
name String? @default("Anonymous")
137+
role Role @default(USER)
138+
profile Profile?
139+
shared_profile Profile? @relation("shared")
140+
posts Post[]
141+
createdAt DateTime @default(now())
142+
updatedAt DateTime @updatedAt
143+
jsonData Json?
144+
balance Decimal @default(0.00)
145+
isActive Boolean @default(true)
146+
bigCounter BigInt @default(0)
147+
bytes Bytes?
148+
149+
@@index([role])
150+
}
151+
152+
model Profile {
153+
id Int @id @default(autoincrement())
154+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
155+
userId String @unique
156+
user_shared User @relation("shared", fields: [shared_userId], references: [id], onDelete: Cascade)
157+
shared_userId String @unique
158+
bio String?
159+
avatarUrl String?
160+
}
161+
162+
model Tag {
163+
id Int @id @default(autoincrement())
164+
name String @unique
165+
posts PostTag[]
166+
createdAt DateTime @default(now())
167+
168+
@@index([name], name: "tag_name_idx")
169+
}
170+
171+
enum Role {
172+
USER
173+
ADMIN
174+
MODERATOR
175+
}`,
176+
);
177+
console.log(workDir)
178+
runCli('format', workDir);
179+
runCli('db push', workDir);
180+
const schemaFile = path.join(workDir, 'zenstack/schema.zmodel');
181+
const { model } = await loadSchemaDocument(schemaFile, { returnServices: true });
182+
const originalSchema = generator.generate(model);
183+
fs.writeFileSync(path.join(workDir, 'zenstack/schema.zmodel'), getDefaultPrelude());
184+
185+
runCli('db pull --indent 4 --field-casing=camel', workDir);
186+
expect(getSchema(workDir)).toEqual(originalSchema);
187+
});
96188
});

packages/cli/test/utils.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,7 @@ function getTestDbName(provider: string) {
3434
);
3535
}
3636

37-
export function createProject(
38-
zmodel: string,
39-
options?: { customPrelude?: boolean; provider?: 'sqlite' | 'postgresql' },
40-
) {
37+
export function getDefaultPrelude(options?: { provider?: 'sqlite' | 'postgresql' }) {
4138
const provider = (options?.provider || getTestDbProvider()) ?? 'sqlite';
4239
const dbName = getTestDbName(provider);
4340
const dbUrl =
@@ -46,18 +43,25 @@ export function createProject(
4643
: `postgres://${TEST_PG_CONFIG.user}:${TEST_PG_CONFIG.password}@${TEST_PG_CONFIG.host}:${TEST_PG_CONFIG.port}/${dbName}`;
4744

4845
const ZMODEL_PRELUDE = `datasource db {
49-
provider = "${provider}"
50-
url = "${dbUrl}"
46+
provider = "${provider}"
47+
url = "${dbUrl}"
5148
}
5249
`;
50+
return ZMODEL_PRELUDE;
51+
}
52+
53+
export function createProject(
54+
zmodel: string,
55+
options?: { customPrelude?: boolean; provider?: 'sqlite' | 'postgresql' },
56+
) {
5357
const workDir = createTestProject();
5458
fs.mkdirSync(path.join(workDir, 'zenstack'), { recursive: true });
5559
const schemaPath = path.join(workDir, 'zenstack/schema.zmodel');
56-
fs.writeFileSync(schemaPath, !options?.customPrelude ? `${ZMODEL_PRELUDE}\n${zmodel}` : zmodel);
60+
fs.writeFileSync(schemaPath, !options?.customPrelude ? `${getDefaultPrelude()}\n${zmodel}` : zmodel);
5761
return workDir;
5862
}
5963

6064
export function runCli(command: string, cwd: string) {
6165
const cli = path.join(__dirname, '../dist/index.js');
62-
execSync(`node ${cli} ${command}`, { cwd });
66+
execSync(`node ${cli} ${command}`, { cwd, stdio: 'inherit' });
6367
}

0 commit comments

Comments
 (0)