Repository: Quramy/prisma-fabbrica (ts)

Issue:
Failed to generate when model without PK
**Describe the bug**
プライマリキーを持たないmodelの場合、npx prisma generate実行時に
```
Cannot read properties of undefined (reading 'name')
```
が発生し、generateに失敗してしまいます。

**To Reproduce**
後述するschema.prismaを用意し、npx prisma generateを実行で確認できます。
CompanyEmployeeに、@id、もしくは@@idでPKを設定すれば、発生しないことを確認しています。

**Expected behavior**
generateの正常終了

**Your Schema**

```
// Paste schema.prisma file.
model Company {
  id    String @id
  name  String
  companyEmployees CompanyEmployee[]
}

model Employee {
  id        String   @id
  name      String
  companyEmployees CompanyEmployee?
}

model CompanyEmployee {
  company   Company  @relation(fields: [companyId], references: [id])
  companyId String
  employee  Employee @relation(fields: [employeeId], references: [id])
  employeeId String @unique

  @@unique([companyId, employeeId])
}
```

**Environment (please complete the following information):**

- Database kind [sqlite]
- Node.js version [18.11.0]
- `@prisma/client` version [4.6.1]
- TypeScript version [4.9.3]

**Additional context**
93247ecfd30e4ebd80dcee65acb6cab1154a1363
コードは最新のものから、特に変更を加えず確認しています。

Pull request:
fix: Models without @id nor @@id
It fixes #55 

Patch:
diff --git a/packages/prisma-fabbrica/src/templates/index.ts b/packages/prisma-fabbrica/src/templates/index.ts
index e699c46..c155332 100644
--- a/packages/prisma-fabbrica/src/templates/index.ts
+++ b/packages/prisma-fabbrica/src/templates/index.ts
@@ -11,8 +11,8 @@ export function findPrsimaCreateInputTypeFromModelName(document: DMMF.Document,
   return inputType;
 }
 
-function getIdFieldNames(model: DMMF.Model) {
-  return model.primaryKey?.fields ?? [model.fields.find(f => f.isId)!.name];
+export function getIdFieldNames(model: DMMF.Model) {
+  return model.primaryKey?.fields ?? [model.fields.find(f => f.isId || f.isUnique)!.name];
 }
 
 function filterRequiredFields(inputType: DMMF.InputType) {


Test patch:
diff --git a/packages/prisma-fabbrica/src/templates/getIdFieldNames.test.ts b/packages/prisma-fabbrica/src/templates/getIdFieldNames.test.ts
new file mode 100644
index 0000000..c43e798
--- /dev/null
+++ b/packages/prisma-fabbrica/src/templates/getIdFieldNames.test.ts
@@ -0,0 +1,43 @@
+import { getDMMF } from "@prisma/internals";
+
+import { modelScalarOrEnumFields, getIdFieldNames } from ".";
+
+describe(modelScalarOrEnumFields, () => {
+  it.each([
+    {
+      pattern: "@id",
+      datamodel: `
+        model TestModel {
+          id Int @id
+        }
+      `,
+      expected: ["id"],
+    },
+    {
+      pattern: "no PK but @unique",
+      datamodel: `
+        model TestModel {
+          field1 Int @unique
+          field2 Int @unique
+        }
+      `,
+      expected: ["field1"],
+    },
+    {
+      pattern: "Complex id",
+      datamodel: `
+        model TestModel {
+          field1 Int
+          field2 String
+          @@id([field1, field2])
+        }
+      `,
+      expected: ["field1", "field2"],
+    },
+  ])("generates literal type field for $pattern", async ({ datamodel, expected }) => {
+    const dmmf = await getDMMF({
+      datamodel,
+    });
+    expect(getIdFieldNames(dmmf.datamodel.models[0])).toEqual(expected);
+  });
+});
diff --git a/packages/ts-compile-testing/fixtures/field-variation/__generated__/fabbrica/index.ts b/packages/ts-compile-testing/fixtures/field-variation/__generated__/fabbrica/index.ts
index ed2da5a..6fad97c 100644
--- a/packages/ts-compile-testing/fixtures/field-variation/__generated__/fabbrica/index.ts
+++ b/packages/ts-compile-testing/fixtures/field-variation/__generated__/fabbrica/index.ts
@@ -1,6 +1,7 @@
 import { User } from "./../client";
 import { ComplexIdModel } from "./../client";
 import { FieldTypePatternModel } from "./../client";
+import { NoPkModel } from "./../client";
 import { Role } from "./../client";
 import { Prisma } from "./../client";
 import type { PrismaClient } from "./../client";
@@ -21,6 +22,9 @@ const modelFieldDefinitions: ModelWithFields[] = [{
     }, {
         name: "FieldTypePatternModel",
         fields: []
+    }, {
+        name: "NoPkModel",
+        fields: []
     }];
 type UserScalarOrEnumFields = {
     id: string;
@@ -240,3 +244,62 @@ function defineFieldTypePatternModelFactoryInternal({ defaultData: defaultDataRe
 export function defineFieldTypePatternModelFactory(args: FieldTypePatternModelFactoryDefineOptions = {}) {
     return defineFieldTypePatternModelFactoryInternal(args);
 }
+type NoPkModelScalarOrEnumFields = {
+    id: number;
+};
+type NoPkModelFactoryDefineInput = {
+    id?: number;
+};
+type NoPkModelFactoryDefineOptions = {
+    defaultData?: Resolver<NoPkModelFactoryDefineInput, BuildDataOptions>;
+};
+function autoGenerateNoPkModelScalarsOrEnums({ seq }: {
+    readonly seq: number;
+}): NoPkModelScalarOrEnumFields {
+    return {
+        id: scalarFieldValueGenerator.Int({ modelName: "NoPkModel", fieldName: "id", isId: false, isUnique: true, seq })
+    };
+}
+function defineNoPkModelFactoryInternal({ defaultData: defaultDataResolver }: NoPkModelFactoryDefineOptions) {
+    const seqKey = {};
+    const getSeq = () => getSequenceCounter(seqKey);
+    const screen = createScreener("NoPkModel", modelFieldDefinitions);
+    const build = async (inputData: Partial<Prisma.NoPkModelCreateInput> = {}) => {
+        const seq = getSeq();
+        const requiredScalarData = autoGenerateNoPkModelScalarsOrEnums({ seq });
+        const resolveValue = normalizeResolver<NoPkModelFactoryDefineInput, BuildDataOptions>(defaultDataResolver ?? {});
+        const defaultData = await resolveValue({ seq });
+        const defaultAssociations = {};
+        const data: Prisma.NoPkModelCreateInput = { ...requiredScalarData, ...defaultData, ...defaultAssociations, ...inputData };
+        return data;
+    };
+    const buildList = (inputData: number | Partial<Prisma.NoPkModelCreateInput>[]) => {
+        const list = typeof inputData === "number" ? [...new Array(inputData).keys()].map(() => ({})) : inputData;
+        return Promise.all(list.map(data => build(data)));
+    };
+    const pickForConnect = (inputData: NoPkModel) => ({
+        id: inputData.id
+    });
+    const create = async (inputData: Partial<Prisma.NoPkModelCreateInput> = {}) => {
+        const data = await build(inputData).then(screen);
+        return await getClient<PrismaClient>().noPkModel.create({ data });
+    };
+    const createList = (inputData: number | Partial<Prisma.NoPkModelCreateInput>[]) => {
+        const list = typeof inputData === "number" ? [...new Array(inputData).keys()].map(() => ({})) : inputData;
+        return Promise.all(list.map(data => create(data)));
+    };
+    const createForConnect = (inputData: Partial<Prisma.NoPkModelCreateInput> = {}) => create(inputData).then(pickForConnect);
+    return {
+        _factoryFor: "NoPkModel" as const,
+        build,
+        buildList,
+        buildCreateInput: build,
+        pickForConnect,
+        create,
+        createList,
+        createForConnect,
+    };
+}
+export function defineNoPkModelFactory(args: NoPkModelFactoryDefineOptions = {}) {
+    return defineNoPkModelFactoryInternal(args);
+}
diff --git a/packages/ts-compile-testing/fixtures/field-variation/schema.prisma b/packages/ts-compile-testing/fixtures/field-variation/schema.prisma
index ed96db1..a7d2df9 100644
--- a/packages/ts-compile-testing/fixtures/field-variation/schema.prisma
+++ b/packages/ts-compile-testing/fixtures/field-variation/schema.prisma
@@ -51,3 +51,7 @@ model FieldTypePatternModel {
   requiredBigInt  BigInt
   nullableBigInt  BigInt?
 }
+
+model NoPkModel {
+  id Int @unique
+}