diff --git a/README.md b/README.md index d38df790..7e414a40 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,10 @@ Warthog is now on version 3.0! There were a few breaking changes that you should Expand for Breaking change details

+### Adds `computed` attribute to models + +A `computed` property is one that is fully managed by the server. A good example is a status flag that is managed by a state machine in the backend. You should not be able to modify this on create or update, but should always be able to read it. + ### Switch ID generation library from `shortid` (deprecated) to `nanoid` Per `shortid` [README](https://github.com/dylang/shortid/blob/master/README.md) it is deprecated. diff --git a/examples/01-simple-model/.env b/examples/01-simple-model/.env index 7e3161fb..96308dae 100644 --- a/examples/01-simple-model/.env +++ b/examples/01-simple-model/.env @@ -3,6 +3,6 @@ PGUSER=postgres WARTHOG_API_BASE_URL=http://localhost:4100 WARTHOG_DB_DATABASE=warthog-example-1 WARTHOG_DB_USERNAME=postgres -WARTHOG_DB_PASSWORD= +WARTHOG_DB_PASSWORD=postgres WARTHOG_DB_SYNCHRONIZE=true WARTHOG_FILTER_BY_DEFAULT=true diff --git a/examples/03-one-to-many-relationship/.env b/examples/03-one-to-many-relationship/.env index 80c41631..fae236eb 100644 --- a/examples/03-one-to-many-relationship/.env +++ b/examples/03-one-to-many-relationship/.env @@ -3,6 +3,6 @@ PGUSER=postgres WARTHOG_API_BASE_URL=http://localhost:4100 WARTHOG_DB_DATABASE=warthog-example-3 WARTHOG_DB_USERNAME=postgres -WARTHOG_DB_PASSWORD= +WARTHOG_DB_PASSWORD=postgres WARTHOG_DB_SYNCHRONIZE=true WARTHOG_FILTER_BY_DEFAULT=true diff --git a/examples/04-many-to-many-relationship/.env b/examples/04-many-to-many-relationship/.env index a5390bba..1ccab80c 100644 --- a/examples/04-many-to-many-relationship/.env +++ b/examples/04-many-to-many-relationship/.env @@ -3,6 +3,6 @@ PGUSER=postgres WARTHOG_API_BASE_URL=http://localhost:4100 WARTHOG_DB_DATABASE=warthog-example-4 WARTHOG_DB_USERNAME=postgres -WARTHOG_DB_PASSWORD= +WARTHOG_DB_PASSWORD=postgres WARTHOG_DB_SYNCHRONIZE=true WARTHOG_FILTER_BY_DEFAULT=true diff --git a/examples/05-migrations/.env b/examples/05-migrations/.env index 66ee0a7d..007f8af7 100644 --- a/examples/05-migrations/.env +++ b/examples/05-migrations/.env @@ -3,5 +3,5 @@ PGUSER=postgres WARTHOG_API_BASE_URL=http://localhost:4100 WARTHOG_DB_DATABASE=warthog-example-5 WARTHOG_DB_USERNAME=postgres -WARTHOG_DB_PASSWORD= +WARTHOG_DB_PASSWORD=postgres WARTHOG_FILTER_BY_DEFAULT=true diff --git a/examples/06-base-service/.env b/examples/06-base-service/.env index 17a31d2c..21a1b6a0 100644 --- a/examples/06-base-service/.env +++ b/examples/06-base-service/.env @@ -3,6 +3,6 @@ PGUSER=postgres WARTHOG_API_BASE_URL=http://localhost:4100 WARTHOG_DB_DATABASE=warthog-example-6 WARTHOG_DB_USERNAME=postgres -WARTHOG_DB_PASSWORD= +WARTHOG_DB_PASSWORD=postgres WARTHOG_DB_SYNCHRONIZE=true WARTHOG_FILTER_BY_DEFAULT=true diff --git a/examples/07-feature-flags/.env b/examples/07-feature-flags/.env index bb9a2799..e7d6b409 100644 --- a/examples/07-feature-flags/.env +++ b/examples/07-feature-flags/.env @@ -3,6 +3,6 @@ PGUSER=postgres WARTHOG_API_BASE_URL=http://localhost:4100 WARTHOG_DB_DATABASE=warthog-example-feature-flag WARTHOG_DB_USERNAME=postgres -WARTHOG_DB_PASSWORD= +WARTHOG_DB_PASSWORD=postgres WARTHOG_DB_SYNCHRONIZE=true WARTHOG_FILTER_BY_DEFAULT=true diff --git a/examples/08-performance/.env b/examples/08-performance/.env index e11d56be..e55677c4 100644 --- a/examples/08-performance/.env +++ b/examples/08-performance/.env @@ -3,6 +3,6 @@ PGUSER=postgres WARTHOG_API_BASE_URL=http://localhost:4100 WARTHOG_DB_DATABASE=warthog-example-8 WARTHOG_DB_USERNAME=postgres -WARTHOG_DB_PASSWORD= +WARTHOG_DB_PASSWORD=postgres WARTHOG_DB_SYNCHRONIZE=true WARTHOG_FILTER_BY_DEFAULT=true diff --git a/examples/09-production/.env b/examples/09-production/.env index 87067253..48a2d0fa 100644 --- a/examples/09-production/.env +++ b/examples/09-production/.env @@ -11,7 +11,7 @@ PGUSER=postgres WARTHOG_API_BASE_URL=http://localhost:4100 WARTHOG_DB_DATABASE=warthog-example-9 WARTHOG_DB_HOST=localhost -WARTHOG_DB_PASSWORD= +WARTHOG_DB_PASSWORD=postgres WARTHOG_DB_PORT=5432 WARTHOG_DB_USERNAME=postgres WARTHOG_FILTER_BY_DEFAULT=false diff --git a/examples/10-subscriptions/.env b/examples/10-subscriptions/.env index 7b3583d4..84656fcb 100644 --- a/examples/10-subscriptions/.env +++ b/examples/10-subscriptions/.env @@ -3,7 +3,7 @@ PGUSER=postgres WARTHOG_API_BASE_URL=http://localhost:4100 WARTHOG_DB_DATABASE=warthog-10-subscriptions WARTHOG_DB_USERNAME=postgres -WARTHOG_DB_PASSWORD= +WARTHOG_DB_PASSWORD=postgres WARTHOG_DB_SYNCHRONIZE=true WARTHOG_SUBSCRIPTIONS=true WARTHOG_FILTER_BY_DEFAULT=true diff --git a/examples/11-transactions/.env b/examples/11-transactions/.env index e2da76ff..7e8d83e4 100644 --- a/examples/11-transactions/.env +++ b/examples/11-transactions/.env @@ -3,7 +3,7 @@ PGUSER=postgres WARTHOG_API_BASE_URL=http://localhost:4100 WARTHOG_DB_DATABASE=warthog-11-transactions WARTHOG_DB_USERNAME=postgres -WARTHOG_DB_PASSWORD= +WARTHOG_DB_PASSWORD=postgres WARTHOG_DB_SYNCHRONIZE=true WARTHOG_SUBSCRIPTIONS=true WARTHOG_FILTER_BY_DEFAULT=true diff --git a/examples/14-base-service-v2/.env b/examples/14-base-service-v2/.env index d1236df9..0c83b89a 100644 --- a/examples/14-base-service-v2/.env +++ b/examples/14-base-service-v2/.env @@ -3,6 +3,6 @@ PGUSER=postgres WARTHOG_API_BASE_URL=http://localhost:4100 WARTHOG_DB_DATABASE=warthog-14-base-service-v2 WARTHOG_DB_USERNAME=postgres -WARTHOG_DB_PASSWORD= +WARTHOG_DB_PASSWORD=postgres WARTHOG_DB_SYNCHRONIZE=true WARTHOG_SUBSCRIPTIONS=true diff --git a/src/cli/templates/new/_env.ejs b/src/cli/templates/new/_env.ejs index bf1fd297..9e496cfa 100644 --- a/src/cli/templates/new/_env.ejs +++ b/src/cli/templates/new/_env.ejs @@ -7,7 +7,7 @@ WARTHOG_API_BASE_URL=http://localhost:4000 WARTHOG_DB_DATABASE=<%= props.kebabName %> WARTHOG_DB_HOST=localhost WARTHOG_DB_LOGGING=all -WARTHOG_DB_PASSWORD= +WARTHOG_DB_PASSWORD=postgres WARTHOG_DB_PORT=5432 WARTHOG_DB_SYNCHRONIZE=true WARTHOG_DB_USERNAME=postgres diff --git a/src/cli/templates/new/env.yml.ejs b/src/cli/templates/new/env.yml.ejs index 834ae8f5..587f005c 100644 --- a/src/cli/templates/new/env.yml.ejs +++ b/src/cli/templates/new/env.yml.ejs @@ -20,7 +20,7 @@ local_db: &local_db WARTHOG_DB_DATABASE: warthog-starter-development WARTHOG_DB_HOST: localhost WARTHOG_DB_LOGGING: all - WARTHOG_DB_PASSWORD: '' + WARTHOG_DB_PASSWORD: postgres WARTHOG_DB_PORT: 5432 WARTHOG_DB_SYNCHRONIZE: true WARTHOG_DB_USERNAME: postgres diff --git a/src/core/tests/dotenv-files/.env b/src/core/tests/dotenv-files/.env index 608930d8..5fc7d922 100644 --- a/src/core/tests/dotenv-files/.env +++ b/src/core/tests/dotenv-files/.env @@ -8,6 +8,6 @@ WARTHOG_D=ENV WARTHOG_API_BASE_URL=http://localhost:4100 WARTHOG_DB_DATABASE=warthog-example-1 WARTHOG_DB_USERNAME=postgres -WARTHOG_DB_PASSWORD= +WARTHOG_DB_PASSWORD=postgres WARTHOG_DB_SYNCHRONIZE=true WARTHOG_DB_HOST=localhost diff --git a/src/decorators/OneToOne.ts.bak b/src/decorators/OneToOne.ts.bak new file mode 100644 index 00000000..f79b3586 --- /dev/null +++ b/src/decorators/OneToOne.ts.bak @@ -0,0 +1,16 @@ +import { Field } from 'type-graphql'; +import { JoinTable, ManyToMany as TypeORMManyToMany } from 'typeorm'; +import { composeMethodDecorators, MethodDecoratorFactory } from '../utils'; + +// Note: for many to many relationships, you need to set one item as the "JoinTable" +// therefore, we have 2 separate decorators. Just make sure to add one to one table and +// One to the other in the relationship +export function OneToOne(parentType: any, joinFunc: any, options: any = {}): any { + const factories = [ + JoinTable() as MethodDecoratorFactory, + Field(() => [parentType()], { nullable: true, ...options }) as MethodDecoratorFactory, + TypeORMManyToMany(parentType, joinFunc, options) as MethodDecoratorFactory + ]; + + return composeMethodDecorators(...factories); +} diff --git a/src/metadata/metadata-storage.ts b/src/metadata/metadata-storage.ts index b1fd10e9..bcd875df 100644 --- a/src/metadata/metadata-storage.ts +++ b/src/metadata/metadata-storage.ts @@ -20,12 +20,13 @@ export type FieldType = export interface DecoratorCommonOptions { apiOnly?: boolean; + computed?: boolean; // Means the back end will manage this column fully. Can read it, but no writing dbOnly?: boolean; description?: string; editable?: boolean; filter?: boolean | WhereOperator[]; nullable?: boolean; - readonly?: boolean; + readonly?: boolean; // Setting this will set it readonly with TypeORM meaning you can't use update methods at all sort?: boolean; writeonly?: boolean; } diff --git a/src/schema/SchemaGenerator.ts b/src/schema/SchemaGenerator.ts index 879df2a4..a1837bdc 100644 --- a/src/schema/SchemaGenerator.ts +++ b/src/schema/SchemaGenerator.ts @@ -233,7 +233,7 @@ export class SchemaGenerator { const modelColumns = this.getColumnsForModel(model); modelColumns.forEach((column: ColumnMetadata) => { - if (column.readonly) { + if (column.readonly || column.computed) { return; } let graphQLDataType = this.columnToGraphQLDataType(column); @@ -279,7 +279,7 @@ export class SchemaGenerator { const modelColumns = this.getColumnsForModel(model); modelColumns.forEach((column: ColumnMetadata) => { - if (column.readonly) { + if (column.readonly || column.computed) { return; } diff --git a/src/test/functional/__snapshots__/schema.test.ts.snap b/src/test/functional/__snapshots__/schema.test.ts.snap index 288fe23f..e55144c3 100644 --- a/src/test/functional/__snapshots__/schema.test.ts.snap +++ b/src/test/functional/__snapshots__/schema.test.ts.snap @@ -140,6 +140,7 @@ type KitchenSink { integerField: Int! booleanField: Boolean! floatField: Float! + computedColumn: String! jsonField: JSONObject idField: ID stringEnumField: StringEnum @@ -212,6 +213,8 @@ enum KitchenSinkOrderByInput { booleanField_DESC floatField_ASC floatField_DESC + computedColumn_ASC + computedColumn_DESC idField_ASC idField_DESC stringEnumField_ASC @@ -323,6 +326,11 @@ input KitchenSinkWhereInput { floatField_lt: Float floatField_lte: Float floatField_in: [Float!] + computedColumn_eq: String + computedColumn_contains: String + computedColumn_startsWith: String + computedColumn_endsWith: String + computedColumn_in: [String!] jsonField_json: JSONObject idField_eq: ID idField_in: [ID!] diff --git a/src/test/generated/binding.ts b/src/test/generated/binding.ts index cfc1a6df..c866c4e1 100644 --- a/src/test/generated/binding.ts +++ b/src/test/generated/binding.ts @@ -83,6 +83,8 @@ export type KitchenSinkOrderByInput = 'id_ASC' | 'booleanField_DESC' | 'floatField_ASC' | 'floatField_DESC' | + 'computedColumn_ASC' | + 'computedColumn_DESC' | 'idField_ASC' | 'idField_DESC' | 'stringEnumField_ASC' | @@ -285,6 +287,11 @@ export interface KitchenSinkWhereInput { floatField_lt?: Float | null floatField_lte?: Float | null floatField_in?: Float[] | Float | null + computedColumn_eq?: String | null + computedColumn_contains?: String | null + computedColumn_startsWith?: String | null + computedColumn_endsWith?: String | null + computedColumn_in?: String[] | String | null jsonField_json?: JSONObject | null idField_eq?: ID_Input | null idField_in?: ID_Output[] | ID_Output | null @@ -439,6 +446,7 @@ export interface KitchenSink { integerField: Int booleanField: Boolean floatField: Float + computedColumn: String jsonField?: JSONObject | null idField?: ID_Output | null stringEnumField?: StringEnum | null diff --git a/src/test/generated/classes.ts b/src/test/generated/classes.ts index c6875fdf..6fde4d0b 100644 --- a/src/test/generated/classes.ts +++ b/src/test/generated/classes.ts @@ -146,6 +146,9 @@ export enum KitchenSinkOrderByEnum { floatField_ASC = "floatField_ASC", floatField_DESC = "floatField_DESC", + computedColumn_ASC = "computedColumn_ASC", + computedColumn_DESC = "computedColumn_DESC", + idField_ASC = "idField_ASC", idField_DESC = "idField_DESC", @@ -339,6 +342,21 @@ export class KitchenSinkWhereInput { @TypeGraphQLField(() => [Float], { nullable: true }) floatField_in?: number[]; + @TypeGraphQLField({ nullable: true }) + computedColumn_eq?: string; + + @TypeGraphQLField({ nullable: true }) + computedColumn_contains?: string; + + @TypeGraphQLField({ nullable: true }) + computedColumn_startsWith?: string; + + @TypeGraphQLField({ nullable: true }) + computedColumn_endsWith?: string; + + @TypeGraphQLField(() => [String], { nullable: true }) + computedColumn_in?: string[]; + @TypeGraphQLField(() => GraphQLJSONObject, { nullable: true }) jsonField_json?: JsonObject; diff --git a/src/test/generated/schema.graphql b/src/test/generated/schema.graphql index b72563d0..8a293bf5 100644 --- a/src/test/generated/schema.graphql +++ b/src/test/generated/schema.graphql @@ -137,6 +137,7 @@ type KitchenSink { integerField: Int! booleanField: Boolean! floatField: Float! + computedColumn: String! jsonField: JSONObject idField: ID stringEnumField: StringEnum @@ -209,6 +210,8 @@ enum KitchenSinkOrderByInput { booleanField_DESC floatField_ASC floatField_DESC + computedColumn_ASC + computedColumn_DESC idField_ASC idField_DESC stringEnumField_ASC @@ -320,6 +323,11 @@ input KitchenSinkWhereInput { floatField_lt: Float floatField_lte: Float floatField_in: [Float!] + computedColumn_eq: String + computedColumn_contains: String + computedColumn_startsWith: String + computedColumn_endsWith: String + computedColumn_in: [String!] jsonField_json: JSONObject idField_eq: ID idField_in: [ID!] diff --git a/src/test/modules/kitchen-sink/kitchen-sink.model.ts b/src/test/modules/kitchen-sink/kitchen-sink.model.ts index 0f26787d..4d007b61 100644 --- a/src/test/modules/kitchen-sink/kitchen-sink.model.ts +++ b/src/test/modules/kitchen-sink/kitchen-sink.model.ts @@ -63,6 +63,9 @@ export class KitchenSink extends BaseModel { @FloatField() floatField?: number; + @StringField({ computed: true, default: 'computed' }) + computedColumn?: string; + @JSONField({ nullable: true }) jsonField?: JsonObject; diff --git a/src/test/server-vars.ts b/src/test/server-vars.ts index f912e0d5..be18a662 100644 --- a/src/test/server-vars.ts +++ b/src/test/server-vars.ts @@ -30,7 +30,7 @@ export function getStandardEnvironmentVariables(): StringMap { WARTHOG_DB_MIGRATIONS_DIR: './tmp/test/migrations', WARTHOG_DB_OVERRIDE: 'true', // Set so that we can do DB stuff outside of NODE_ENV=development WARTHOG_DB_USERNAME: 'postgres', - WARTHOG_DB_PASSWORD: '', + WARTHOG_DB_PASSWORD: 'postgres', WARTHOG_DB_SYNCHRONIZE: 'true', WARTHOG_FILTER_BY_DEFAULT: 'true', WARTHOG_GENERATED_FOLDER: './src/test/generated', diff --git a/tools/test.sh b/tools/test.sh index 9017c62a..d41e0974 100755 --- a/tools/test.sh +++ b/tools/test.sh @@ -9,8 +9,8 @@ NODE_ENV=test ./src/test/codegen-test-files.sh if [ -z "$SKIP_DB_CREATION" ] then - NODE_ENV=test PGUSER=postgres ./bin/warthog db:drop - NODE_ENV=test PGUSER=postgres ./bin/warthog db:create + NODE_ENV=test PGUSER=postgres WARTHOG_DB_PASSWORD=postgres ./bin/warthog db:drop + NODE_ENV=test PGUSER=postgres WARTHOG_DB_PASSWORD=postgres ./bin/warthog db:create fi # Forward command line args to the jest command