Skip to content

Commit

Permalink
feat: Option to disable atomic number operations
Browse files Browse the repository at this point in the history
  • Loading branch information
unlight committed Sep 4, 2020
1 parent 207785f commit 3319ff9
Show file tree
Hide file tree
Showing 10 changed files with 234 additions and 97 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

Generate object types, inputs, args, etc. from prisma schema file for usage with @nestjs/graphql module.

## Features

- Generates only necessary imports
- Combines zoo of nested/nullable filters
- Updates source code of existing files
- Do not generate resolvers, since it's application specific

## Install

```sh
Expand Down Expand Up @@ -34,6 +41,10 @@ npx prisma generate
- `{name}` - name of model/input/arg
- `{dasherizedName}` - dashed-case name of model/input/arg without suffix
- `{type}` - short type name (model, input)
- `combineScalarFilters` - Combine nested/nullable scalar filters to single
(default: `true`)
- `atomicNumberOperations` - Atomic number operations,
`false` - disabled (default), `true` - enabled

## Resources

Expand Down
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@
}
},
"dependencies": {
"@prisma/generator-helper": "^2.6.1",
"@prisma/generator-helper": "^2.6.2",
"boolean": "^3.0.1",
"get-relative-path": "^1.0.2",
"pupa": "^2.0.1",
"to-kebab": "^1.0.7",
Expand All @@ -66,12 +67,12 @@
"@nestjs/graphql": "^7.6.0",
"@nestjs/platform-express": "^7.4.4",
"@paljs/plugins": "^1.2.6",
"@prisma/cli": "^2.6.1",
"@prisma/cli": "^2.6.2",
"@prisma/client": ">=2.6",
"@semantic-release/changelog": "^5.0.1",
"@semantic-release/git": "^9.0.0",
"@types/mocha": "^8.0.3",
"@types/node": "^14.6.3",
"@types/node": "^14.6.4",
"@typescript-eslint/eslint-plugin": "^4.0.1",
"@typescript-eslint/parser": "^4.0.1",
"apollo-server-express": "^2.17.0",
Expand Down
1 change: 1 addition & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ datasource database {

generator client {
provider = "prisma-client-js"
previewFeatures = ["atomicNumberOperations"]
}

generator nestgraphql {
Expand Down
1 change: 0 additions & 1 deletion src/generate-input.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { Project, QuoteKind, SourceFile } from 'ts-morph';

import { generateInput } from './generate-input';
import { generatorOptions, stringContains, stringNotContains } from './testing';
import { schemaOutputToInput } from './type-utils';

describe('generate inputs', () => {
let sourceFile: SourceFile;
Expand Down
1 change: 0 additions & 1 deletion src/generate-input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ export function generateInput(args: GenerateInputArgs) {
});
generateGraphqlImport({ name: 'Field', sourceFile, moduleSpecifier: '@nestjs/graphql' });
for (const field of inputType.fields) {
// console.log('field', field);
const inputType = getMatchingInputType(field.inputType);
const modelField = model?.fields.find((f) => f.name === field.name);
const nullable =
Expand Down
62 changes: 61 additions & 1 deletion src/generate.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import assert from 'assert';
import { SourceFile } from 'ts-morph';
import { PropertyDeclaration, SourceFile } from 'ts-morph';

import { generate } from './generate';
import { generatorOptions, stringContains } from './testing';
Expand Down Expand Up @@ -172,4 +172,64 @@ describe('main generate', () => {
const struct = decorator.getStructure();
assert.strictEqual(struct.arguments?.[0], '() => Boolean');
});

it('get rid of atomic number operations', async () => {
await getResult({
atomicNumberOperations: false,
schema: `
model User {
id String @id
age Int
rating Float?
}
`,
});

[
'float-field-update-operations.input.ts',
'int-field-update-operations.input.ts',
'string-field-update-operations.input.ts',
].forEach((file) => {
assert(
!sourceFiles.find((s) => s.getFilePath().endsWith(file)),
`File ${file} should not exists`,
);
});

sourceFile = sourceFiles.find((s) => s.getFilePath().endsWith('user-update.input.ts'));
assert(sourceFile);

const classDeclaration = sourceFile.getClass('UserUpdateInput');
assert(classDeclaration);

const id = classDeclaration.getProperty('id')?.getStructure();
assert(id);
assert.equal(id.type, 'string');
let args = classDeclaration
.getProperty('id')
?.getDecorator('Field')
?.getArguments()
.map((a) => a.getText());
assert.equal(args?.[0], '() => String');

const age = classDeclaration.getProperty('age')?.getStructure();
assert(age);
assert.equal(age.type, 'number');
args = classDeclaration
.getProperty('age')
?.getDecorator('Field')
?.getArguments()
.map((a) => a.getText());
assert.equal(args?.[0], '() => Int');

const rating = classDeclaration.getProperty('rating')?.getStructure();
assert(rating);
assert.equal(rating.type, 'number | null');
args = classDeclaration
.getProperty('rating')
?.getDecorator('Field')
?.getArguments()
.map((a) => a.getText());
assert.equal(args?.[0], '() => Float');
});
});
8 changes: 6 additions & 2 deletions src/generate.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { DMMF as PrismaDMMF } from '@prisma/client/runtime/dmmf-types';
import { GeneratorOptions } from '@prisma/generator-helper';
import assert from 'assert';
import { boolean } from 'boolean';
import { existsSync, promises as fs } from 'fs';
import { join } from 'path';
import { Project, QuoteKind, SourceFile } from 'ts-morph';
Expand All @@ -9,7 +10,7 @@ import { generateEnum } from './generate-enum';
import { FileType, generateFileName, getFeatureName } from './generate-file-name';
import { generateInput } from './generate-input';
import { generateModel } from './generate-model';
import { mutateScalarInputs } from './mutate-scalar-inputs';
import { mutateFilters } from './mutate-filters';
import { schemaOutputToInput } from './type-utils';

type GenerateArgs = GeneratorOptions & {
Expand Down Expand Up @@ -68,7 +69,10 @@ export async function generate(args: GenerateArgs) {
}
// Generate inputs
const inputTypes = prismaClientDmmf.schema.inputTypes.filter(
mutateScalarInputs(prismaClientDmmf.schema.inputTypes),
mutateFilters(prismaClientDmmf.schema.inputTypes, {
combineScalarFilters: boolean(generator.config.combineScalarFilters ?? true),
atomicNumberOperations: boolean(generator.config.atomicNumberOperations ?? false),
}),
);
// Create aggregate inputs
const aggregateInputs = prismaClientDmmf.schema.outputTypes
Expand Down
148 changes: 148 additions & 0 deletions src/mutate-filters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import { DMMF as PrismaDMMF } from '@prisma/client/runtime/dmmf-types';

type MutateFiltersOptions = {
atomicNumberOperations?: boolean;
combineScalarFilters?: boolean;
};

export function mutateFilters(inputTypes: PrismaDMMF.InputType[], options: MutateFiltersOptions) {
const mutations = [
options.combineScalarFilters && [
combineScalarFilters(inputTypes, [
'StringFilter',
'NullableStringFilter',
'StringNullableFilter',
'NestedStringNullableFilter',
'NestedStringFilter',
]),
combineScalarFilters(inputTypes, [
'BooleanFilter',
'NullableBooleanFilter',
'BoolNullableFilter',
'NestedBoolNullableFilter',
]),
combineScalarFilters(inputTypes, [
'IntFilter',
'NullableIntFilter',
'IntNullableFilter',
'NestedIntNullableFilter',
'NestedIntFilter',
]),
combineScalarFilters(inputTypes, [
'FloatFilter',
'NullableFloatFilter',
'FloatNullableFilter',
'NestedFloatNullableFilter',
'NestedFloatFilter',
]),
combineScalarFilters(inputTypes, [
'DateTimeFilter',
'NullableDateTimeFilter',
'DateTimeNullableFilter',
'NestedDateTimeNullableFilter',
'NestedDateTimeFilter',
]),
combineScalarFilters(inputTypes, [
'StringFieldUpdateOperationsInput',
'NullableStringFieldUpdateOperationsInput',
]),
combineScalarFilters(inputTypes, [
'IntFieldUpdateOperationsInput',
'NullableIntFieldUpdateOperationsInput',
]),
combineScalarFilters(inputTypes, [
'FloatFieldUpdateOperationsInput',
'NullableFloatFieldUpdateOperationsInput',
]),
combineScalarFilters(inputTypes, [
'BoolFieldUpdateOperationsInput',
'NullableBoolFieldUpdateOperationsInput',
]),
combineScalarFilters(inputTypes, [
'DateTimeFieldUpdateOperationsInput',
'NullableDateTimeFieldUpdateOperationsInput',
]),
],
!options.atomicNumberOperations &&
noAtomicNumberOperations([
'StringFieldUpdateOperationsInput',
'NullableStringFieldUpdateOperationsInput',
'IntFieldUpdateOperationsInput',
'NullableIntFieldUpdateOperationsInput',
'FloatFieldUpdateOperationsInput',
'NullableFloatFieldUpdateOperationsInput',
'BoolFieldUpdateOperationsInput',
'NullableBoolFieldUpdateOperationsInput',
'DateTimeFieldUpdateOperationsInput',
'NullableDateTimeFieldUpdateOperationsInput',
]),
];

return function (inputType: PrismaDMMF.InputType) {
for (const mutation of mutations.filter(Boolean).flat()) {
const result = mutation && mutation(inputType);
if (!result) {
return false;
}
inputType = result;
}
return inputType;
};
}

function combineScalarFilters(inputTypes: PrismaDMMF.InputType[], names: string[]) {
let counter = 0;
let type: PrismaDMMF.InputType | undefined;
const [main] = names;
for (const name of names) {
type = inputTypes.find((t) => t.name === name);
if (type) {
type = mutateFieldsType(type, names);
break;
}
}

return (inputType: PrismaDMMF.InputType) => {
if (!type) {
return inputType;
}
if (names.includes(inputType.name)) {
counter++;
if (counter > 1) {
return false;
}
inputType.name = main;
inputType.fields = type.fields;
return inputType;
}
inputType = mutateFieldsType(inputType, names);
return inputType;
};

function mutateFieldsType(inputType: PrismaDMMF.InputType, names: string[]) {
const [main] = names;
inputType.fields.forEach((field) => {
field.inputType.forEach((input) => {
if (names.includes(String(input.type))) {
input.type = main;
}
});
});
return inputType;
}
}

function noAtomicNumberOperations(names: string[]) {
return (inputType: PrismaDMMF.InputType) => {
if (names.includes(inputType.name)) {
return false;
}
inputType.fields = inputType.fields.map((field) => {
field.inputType = field.inputType.filter((inputType) => {
return !names.includes(String(inputType.type));
});
return field;
});
return inputType;
};
}
Loading

0 comments on commit 3319ff9

Please sign in to comment.