diff --git a/examples/1-simple-model/README.md b/examples/1-simple-model/README.md index 9aee10ef..9f1c2ed2 100644 --- a/examples/1-simple-model/README.md +++ b/examples/1-simple-model/README.md @@ -10,7 +10,6 @@ Running `yarn bootstrap` will do the following: - Install packages - Create the example DB -- Seed the database with test data ## Running the App diff --git a/examples/1-simple-model/src/index.ts b/examples/1-simple-model/src/index.ts index f2453c61..3c8cf12f 100644 --- a/examples/1-simple-model/src/index.ts +++ b/examples/1-simple-model/src/index.ts @@ -7,17 +7,10 @@ dotenv.config(); import { App } from '../../../src/'; async function bootstrap() { - const app = new App( - // Path written in generated classes - { - container: Container, - warthogImportPath: '../../../src' - }, - { - cache: true, - synchronize: true - } - ); + const app = new App({ + container: Container, + warthogImportPath: '../../../src' // Path written in generated classes + }); await app.start(); } diff --git a/examples/2-complex-example/src/app.ts b/examples/2-complex-example/src/app.ts index 7b4a053d..fb511a9c 100644 --- a/examples/2-complex-example/src/app.ts +++ b/examples/2-complex-example/src/app.ts @@ -7,17 +7,11 @@ import { App, AppOptions } from '../../../src/'; export function getApp(appOptions: Partial = {}, dbOptions: any = {}) { return new App( - // Path written in generated classes { container: Container, - warthogImportPath: '../../../src', + warthogImportPath: '../../../src', // Path written in generated classes ...appOptions }, - { - cache: true, - synchronize: true, - // dropSchema: true, - ...dbOptions - } + dbOptions ); } diff --git a/examples/3-relationships/README.md b/examples/3-relationships/README.md index ddaf4cb6..e48e9331 100644 --- a/examples/3-relationships/README.md +++ b/examples/3-relationships/README.md @@ -10,7 +10,6 @@ Running `yarn bootstrap` will do the following: - Install packages - Create the example DB -- Seed the database ## Running the App diff --git a/examples/3-relationships/src/index.ts b/examples/3-relationships/src/index.ts index 63752868..3c8cf12f 100644 --- a/examples/3-relationships/src/index.ts +++ b/examples/3-relationships/src/index.ts @@ -7,16 +7,10 @@ dotenv.config(); import { App } from '../../../src/'; async function bootstrap() { - const app = new App( - { - container: Container, - warthogImportPath: '../../../src' // Path written in generated classes - }, - { - cache: true, - synchronize: true - } - ); + const app = new App({ + container: Container, + warthogImportPath: '../../../src' // Path written in generated classes + }); await app.start(); } diff --git a/src/core/BaseModel.ts b/src/core/BaseModel.ts index f7e08244..d615d75f 100644 --- a/src/core/BaseModel.ts +++ b/src/core/BaseModel.ts @@ -1,29 +1,29 @@ import * as shortid from 'shortid'; -import { Field, Int, InterfaceType, ObjectType } from 'type-graphql'; +import { Field, ID, Int, InterfaceType, ObjectType } from 'type-graphql'; import { BeforeInsert, Column, CreateDateColumn, PrimaryColumn, UpdateDateColumn, VersionColumn } from 'typeorm'; -import { ID } from '.'; +import { IDType } from './types'; // tslint:disable:max-classes-per-file // This interface adds all of the base type-graphql fields to our BaseClass @InterfaceType() export abstract class BaseGraphQLObject { - @Field(type => String) - id!: ID; + @Field(type => ID) + id!: IDType; @Field() createdAt!: Date; - @Field() createdById?: ID; + @Field() createdById?: IDType; @Field({ nullable: true }) updatedAt?: Date; @Field({ nullable: true }) - updatedById?: ID; + updatedById?: IDType; @Field({ nullable: true }) deletedAt?: Date; @Field({ nullable: true }) - deletedById?: ID; + deletedById?: IDType; @Field(type => Int) version!: number; @@ -33,20 +33,20 @@ export abstract class BaseGraphQLObject { @ObjectType({ implements: BaseGraphQLObject }) export abstract class BaseModel implements BaseGraphQLObject { @PrimaryColumn({ type: String }) - id!: ID; + id!: IDType; @CreateDateColumn() createdAt!: Date; - @Column() createdById!: ID; + @Column() createdById!: IDType; @UpdateDateColumn({ nullable: true }) updatedAt?: Date; @Column({ nullable: true }) - updatedById?: ID; + updatedById?: IDType; @Column({ nullable: true }) deletedAt?: Date; @Column({ nullable: true }) - deletedById?: ID; + deletedById?: IDType; @VersionColumn() version!: number; diff --git a/src/core/app.ts b/src/core/app.ts index d728100e..96cd6885 100644 --- a/src/core/app.ts +++ b/src/core/app.ts @@ -39,6 +39,10 @@ export class App { schema?: GraphQLSchema; constructor(private appOptions: AppOptions, private dbOptions: Partial = {}) { + if (!process.env.NODE_ENV) { + throw new Error("NODE_ENV must be set - use 'development' locally"); + } + if (this.appOptions.container) { // register 3rd party IOC container TypeGraphQLUseContainer(this.appOptions.container); @@ -52,6 +56,9 @@ export class App { async establishDBConnection(): Promise { if (!this.connection) { + if (typeof this.dbOptions.synchronize === 'undefined') { + this.dbOptions = { ...this.dbOptions, synchronize: process.env.NODE_ENV === 'development' }; + } this.connection = await createDBConnection(this.dbOptions); } @@ -150,14 +157,6 @@ export class App { console.log(`🚀 Server ready at http://${this.appHost}:${this.appPort}${this.graphQLServer.graphqlPath}`) ); - // // Configure server options - // const serverOptions: Options = { - // endpoint: '/graphql', - // formatError: formatArgumentValidationError, - // playground: '/playground', - // port: this.appPort - // }; - return this; } diff --git a/src/core/types.ts b/src/core/types.ts index 5aa3ed4f..8fa5389d 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -3,20 +3,20 @@ export type Omit = Pick>; export type StringMap = { [key: string]: string }; export type DateTime = string; -export type ID = string; +export type IDType = string; export interface BaseEntity { - id: ID; + id: IDType; [key: string]: any; } export type WhereInput = { - id_eq?: ID; - id_in?: ID[]; + id_eq?: IDType; + id_in?: IDType[]; }; export type DeleteReponse = { - id: ID; + id: IDType; }; export interface ClassType { diff --git a/src/schema/SchemaGenerator.ts b/src/schema/SchemaGenerator.ts index ff272294..6ccc0edc 100644 --- a/src/schema/SchemaGenerator.ts +++ b/src/schema/SchemaGenerator.ts @@ -25,7 +25,7 @@ export class SchemaGenerator { // This file has been auto-generated by Warthog. Do not update directly as it // will be re-written. If you need to change this file, update models or add // new TypeGraphQL objects - import { ArgsType, Field, InputType } from 'type-graphql'; + import { ArgsType, Field, ID, InputType } from 'type-graphql'; import { registerEnumType } from 'type-graphql'; import { BaseWhereInput, PaginationArgs } from '${warthogImportPath}'; ${entityListToImports(entities).join('')} diff --git a/src/schema/TypeORMConverter.ts b/src/schema/TypeORMConverter.ts index f5e5a507..09c7f876 100644 --- a/src/schema/TypeORMConverter.ts +++ b/src/schema/TypeORMConverter.ts @@ -1,4 +1,12 @@ -import { GraphQLInt, GraphQLScalarType, GraphQLString, GraphQLBoolean, GraphQLFloat, GraphQLEnumType } from 'graphql'; +import { + GraphQLInt, + GraphQLScalarType, + GraphQLString, + GraphQLBoolean, + GraphQLFloat, + GraphQLEnumType, + GraphQLID +} from 'graphql'; import { EntityMetadata } from 'typeorm'; import { ColumnMetadata } from 'typeorm/metadata/ColumnMetadata'; import { UniqueMetadata } from 'typeorm/metadata/UniqueMetadata'; @@ -187,13 +195,13 @@ export function entityToWhereInput(entity: EntityMetadata): string { // TODO: for foreign key fields, only allow the same filters as ID below // Example: photo.userId: String - if (column.isPrimary) { + if (column.isPrimary || graphqlType === GraphQLID) { fieldTemplates += ` - @Field({ nullable: true }) - ${column.propertyName}_eq?: ${tsType}; + @Field(type => ${graphqlType},{ nullable: true }) + ${column.propertyName}_eq?: string; @Field(type => [${graphqlType}], { nullable: true }) - ${column.propertyName}_in?: ${tsType}[]; + ${column.propertyName}_in?: string[]; `; } else if (graphqlType === GraphQLString) { // TODO: do we need NOT? @@ -330,6 +338,8 @@ export function columnToGraphQLType(column: ColumnMetadata): GraphQLScalarType | const enumObject = getMetadataStorage().getEnum(column.entityMetadata.name, column.propertyName); if (enumObject) { return enumObject.name; + } else if (column.propertyName.match(/Id$/)) { + return GraphQLID; } // Some types have a name attribute diff --git a/src/tgql/BaseResolver.ts b/src/tgql/BaseResolver.ts index f4a198d8..3c6379e4 100644 --- a/src/tgql/BaseResolver.ts +++ b/src/tgql/BaseResolver.ts @@ -143,15 +143,11 @@ export class BaseResolver { return { id: where.id }; } - // TODO: boolean logic - AND, OR, NOT // extends WhereInput processWhereOptions(where: W) { const whereOptions: { [key: string]: FindOperator } = {}; Object.keys(where).forEach(k => { const key = k as keyof W; - if (key === 'AND' || key === 'OR' || key === 'not') { - throw new Error('Boolean logic not yet supported'); - } const [attr, operator] = getFindOperator(String(key), where[key]); whereOptions[attr] = operator; }); diff --git a/src/tgql/DeleteResponse.ts b/src/tgql/DeleteResponse.ts index 3be5e28c..79f5de32 100644 --- a/src/tgql/DeleteResponse.ts +++ b/src/tgql/DeleteResponse.ts @@ -1,19 +1,19 @@ -import { Field, InterfaceType, ObjectType } from 'type-graphql'; +import { Field, ID, InterfaceType, ObjectType } from 'type-graphql'; -import { ID } from '../core'; +import { IDType } from '../core'; // tslint:disable:max-classes-per-file @InterfaceType() export abstract class DeleteResponse { - @Field(type => String) - id!: ID; + @Field(type => ID) + id!: IDType; } @ObjectType() export class StandardDeleteResponse { - @Field(type => String) - id!: ID; + @Field(type => ID) + id!: IDType; } // tslint:enable:max-classes-per-file