Skip to content

Commit

Permalink
feat: align model parsing with parsing for input types and output types
Browse files Browse the repository at this point in the history
  • Loading branch information
rfermann committed Apr 16, 2021
1 parent b944c8c commit e365523
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 48 deletions.
14 changes: 10 additions & 4 deletions src/Handlers/BaseHandler/BaseFileGenerator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { DecoratorStructure, OptionalKind, PropertyDeclarationStructure, So
import { IndentationText, NewLineKind, Project, StructureKind } from "ts-morph";

import type { GeneratorConfig } from "../../../GeneratorConfig";
import type { Field } from "../../../types";
import type { Field, ObjectTypes } from "../../../types";
import { NestJSTypes, TypeEnum } from "../../../types";
import { comparePrimitiveValues } from "../compareFunctions";

Expand Down Expand Up @@ -33,7 +33,7 @@ export class BaseFileGenerator {
addEnumImports({ enums, sourceFile, type }: { enums: string[]; sourceFile: SourceFile; type: TypeEnum }): void {
let moduleSpecifier = "";

if (type === TypeEnum.InputType || type === TypeEnum.ModelType) {
if (type === TypeEnum.ModelType) {
moduleSpecifier = `../${this._config.paths.enums}`;
}

Expand Down Expand Up @@ -107,7 +107,13 @@ export class BaseFileGenerator {
}

// eslint-disable-next-line class-methods-use-this
getClassDecorator(documentation: string | undefined): OptionalKind<DecoratorStructure>[] {
getClassDecorator({
decoratorType,
documentation,
}: {
decoratorType: ObjectTypes;
documentation?: string;
}): OptionalKind<DecoratorStructure>[] {
return [
{
arguments: [
Expand All @@ -119,7 +125,7 @@ export class BaseFileGenerator {
.write("}"),
],
kind: StructureKind.Decorator,
name: NestJSTypes.ObjectType,
name: decoratorType,
},
];
}
Expand Down
34 changes: 25 additions & 9 deletions src/Handlers/BaseHandler/BaseParser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ interface ParseField {
location: DMMF.FieldLocation;
name: string;
relationName?: string;
type: string;
type: DMMF.OutputType | DMMF.SchemaEnum | string;
}
interface TSField {
isList: boolean;
Expand Down Expand Up @@ -44,17 +44,30 @@ export class BaseParser {
this.dmmf = dmmf;
}

getEnumImports({ enumImports, field: { kind, type } }: { enumImports: Set<string>; field: DMMF.Field }): Set<string> {
if (kind === "enum") {
getEnumImports({
enumImports,
field: { location, type },
}: {
enumImports: Set<string>;
field: {
location: DMMF.FieldLocation;
type: DMMF.ArgType;
};
}): Set<string> {
if (location === "enumTypes") {
enumImports.add(this.getEnumName(type));
}

return enumImports;
}

// eslint-disable-next-line class-methods-use-this
getEnumName(name: string): string {
return `${name.replace("Enum", "")}Enum`;
getEnumName(type: DMMF.ArgType): string {
if (typeof type === "string") {
return `${type.replace("Enum", "")}Enum`;
}

return `${type.name.replace("Enum", "")}Enum`;
}

getGraphqlScalarImports({
Expand All @@ -72,15 +85,18 @@ export class BaseParser {
}

getJsonImports({
field: { kind, type },
field: { location, type },
jsonImports,
tsType,
}: {
field: DMMF.Field;
field: {
location: DMMF.FieldLocation;
type: string;
};
jsonImports: Set<string>;
tsType: string;
}): Set<string> {
if (kind === "scalar" && this.jsonImports.has(type)) {
if (location === "scalar" && this.jsonImports.has(type)) {
jsonImports.add(type);
}

Expand Down Expand Up @@ -122,7 +138,7 @@ export class BaseParser {

return {
documentation,
graphQLType: this.parseGraphQLType([{ isList, location, type }]),
graphQLType: this.parseGraphQLType([{ isList, location, type: type as DMMF.ArgType }]),
isNullable,
isRequired,
name,
Expand Down
2 changes: 1 addition & 1 deletion src/Handlers/ModelHandler/__snapshots__/index.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export class User {
@Field(() => [Session], {
nullable: true
})
Session?: Session[];
Session?: Session[] | null;
}
"
`;
4 changes: 3 additions & 1 deletion src/Handlers/ModelHandler/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ describe("ModelHandler", () => {
basePath: `${process.cwd()}/fixtures/modelHandler/fixtures/output3`,
paths: {
enums: "enums",
// inputTypes: "",
model: "model",
// shared: "",
},
prismaClientImportPath: `${process.cwd()}/node_modules/@prisma/client`,
};
Expand Down Expand Up @@ -75,7 +77,7 @@ describe("ModelHandler", () => {
@Field(() => User, {
nullable: true
})
user?: User;
user?: User | null;
@Field(() => Int, {
nullable: false
Expand Down
81 changes: 49 additions & 32 deletions src/Handlers/ModelHandler/index.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,13 @@
import type { DMMF } from "@prisma/generator-helper";

import type { Enum, Field, Model } from "../../types";
import { NestJSTypes, TypeEnum } from "../../types";
import { NestJSTypes, ObjectTypes, TypeEnum } from "../../types";
import { BaseHandler } from "../BaseHandler";

const mapKindToLocation = (kind: DMMF.FieldKind): DMMF.FieldLocation => {
switch (kind) {
case "enum":
return "enumTypes";

case "object":
return "inputObjectTypes";

case "scalar":
return "scalar";

default:
/* istanbul ignore next */
throw new Error(`Can't map kind ${kind as string} to a location`);
}
};

export class ModelHandler extends BaseHandler {
private readonly _models: Model[] = [];

// eslint-disable-next-line max-lines-per-function
async createFiles(): Promise<void> {
this._models.forEach(
({
Expand All @@ -39,7 +23,7 @@ export class ModelHandler extends BaseHandler {
const sourceFile = this.baseFileGenerator.createSourceFile(`${name}/${this.config.paths.model}`);

this.baseFileGenerator.addNestJSImports({
nestJSImports: [NestJSTypes.Field, NestJSTypes.ObjectType],
nestJSImports: [NestJSTypes.Field, ObjectTypes.ObjectType],
sourceFile,
});

Expand Down Expand Up @@ -68,7 +52,10 @@ export class ModelHandler extends BaseHandler {
}

sourceFile.addClass({
decorators: this.baseFileGenerator.getClassDecorator(documentation),
decorators: this.baseFileGenerator.getClassDecorator({
decoratorType: ObjectTypes.ObjectType,
documentation,
}),
docs: typeof documentation === "string" ? [documentation] : [],
isExported: true,
name,
Expand All @@ -82,29 +69,58 @@ export class ModelHandler extends BaseHandler {
await this.baseFileGenerator.save();
}

// eslint-disable-next-line max-lines-per-function
parse(enums: Enum[]): void {
this.baseParser.dmmf.datamodel.models.forEach(({ documentation, name, fields: modelFields }) => {
// eslint-disable-next-line max-lines-per-function
this.baseParser.dmmf.schema.outputObjectTypes.model.forEach(({ name, fields: modelFields }) => {
const fields: Field[] = [];
let enumImports: Set<string> = new Set();
let graphqlScalarImports: Set<string> = new Set();
let jsonImports: Set<string> = new Set();
let modelImports: Set<string> = new Set();
const modelImports: Set<string> = new Set();
const nestJSImports: Set<string> = new Set();
const datamodel = this.baseParser.dmmf.datamodel.models.find((model) => model.name === name);

modelFields.forEach(({ isNullable: isNullableBase, name: fieldName, outputType }) => {
const { location, type } = outputType as { location: DMMF.FieldLocation; type: string };
/* istanbul ignore next */
const fieldModel = datamodel?.fields.find((f) => f.name === fieldName);
const isNullable = outputType.location === "outputObjectTypes" ? true : isNullableBase;

const parsedField = this.baseParser.parseField({
enums,
field: {
/* istanbul ignore next */
documentation: fieldModel?.documentation,
isInputType: false,
isList: outputType.isList,
isRequired: !isNullable,
location,
name: fieldName,
type,
},
});

modelFields.forEach((field) => {
const location = mapKindToLocation(field.kind);
const parsedField = this.baseParser.parseField({ enums, field: { ...field, isInputType: false, location } });

enumImports = this.baseParser.getEnumImports({ enumImports, field });
enumImports = this.baseParser.getEnumImports({
enumImports,
field: { location, type },
});
graphqlScalarImports = this.baseParser.getGraphqlScalarImports({
graphqlScalarImports,
type: parsedField.graphQLType,
});
jsonImports = this.baseParser.getJsonImports({ field, jsonImports, tsType: parsedField.tsType });
modelImports = this.baseParser.getModelImports({ field, modelImports });

if (this.baseParser.nestJSImports.has(field.type)) {
nestJSImports.add(field.type);
jsonImports = this.baseParser.getJsonImports({
field: { location, type },
jsonImports,
tsType: parsedField.tsType,
});
if (location === "outputObjectTypes") {
modelImports.add(type);
}

if (this.baseParser.nestJSImports.has(type)) {
nestJSImports.add(type);
}

if (this.baseParser.nestJSImports.has(parsedField.graphQLType)) {
Expand All @@ -115,7 +131,8 @@ export class ModelHandler extends BaseHandler {
});

this._models.push({
documentation,
/* istanbul ignore next */
documentation: datamodel?.documentation,
enumImports: Array.from(enumImports),
fields,
graphqlScalarImports: Array.from(graphqlScalarImports),
Expand Down
7 changes: 6 additions & 1 deletion src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,14 @@ const enum NestJSTypes {
"ID" = "ID",
"ImportPath" = "@nestjs/graphql",
"Int" = "Int",
"ObjectType" = "ObjectType",
"RegisterEnumType" = "registerEnumType",
}

const enum ObjectTypes {
"InputType" = "InputType",
"ObjectType" = "ObjectType",
}

const enum TypeEnum {
"InputType" = "InputType",
"ModelType" = "ModelType",
Expand All @@ -68,6 +72,7 @@ export {
// InputType,
Model,
NestJSTypes,
ObjectTypes,
TypeEnum,
// OperationTypeEnum as OperationType,
// OutputType,
Expand Down

0 comments on commit e365523

Please sign in to comment.