Giter Site home page Giter Site logo

tripss / nestjs-query Goto Github PK

View Code? Open in Web Editor NEW
139.0 6.0 38.0 45.98 MB

Easy CRUD for GraphQL.

Home Page: https://tripss.github.io/nestjs-query/

License: MIT License

JavaScript 0.11% TypeScript 99.88% Shell 0.01%
crud mongoose nestjs nestjs-graphql sequelize typegoose typeorm

nestjs-query's People

Contributors

aleksey-tk avatar boxcc avatar cbjjensen avatar copystrike avatar cosmin-techoff avatar davidevernizzi avatar dependabot[bot] avatar doug-martin avatar fishtheoriginal avatar gp4ck avatar iamkhalidbashir avatar johannesschobel avatar johndoeplusplus avatar jpv-os avatar khawarizmus avatar koolamusic avatar manlyman29 avatar marian2js avatar meekdenzo avatar mwoelk avatar nicknisi avatar onichandame avatar psteinroe avatar renovate-bot avatar smolinari avatar tripss avatar valentinvignal avatar yiin avatar zackerydev avatar zhangciwu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

nestjs-query's Issues

Introduction example is missing the nestjs graphql driver configuration property

๐Ÿ“š Documentation

If we follow the introduction getting started/install/example from https://tripss.github.io/nestjs-query/docs/introduction/example

When running npm start, there is an error:

[12:04:08] Starting compilation in watch mode...

[12:04:13] Found 0 errors. Watching for file changes.

[Nest] 14765  - 13/01/2023, 12:04:17   ERROR [GraphQLModule] Missing "driver" option. In the latest version of "@nestjs/graphql" package (v10) a new required configuration property called "driver" has been introduced. Check out the official documentation for more details on how to migrate (https://docs.nestjs.com/graphql/migration-guide). Example:

GraphQLModule.forRoot<ApolloDriverConfig>({
  driver: ApolloDriver,
})

/Users/valentin/Perso/Projects/reproducible/node_modules/@nestjs/graphql/dist/graphql.module.js:121
            throw new Error(errorMessage);
                  ^
Error: Missing "driver" option. In the latest version of "@nestjs/graphql" package (v10) a new required configuration property called "driver" has been introduced. Check out the official documentation for more details on how to migrate (https://docs.nestjs.com/graphql/migration-guide). Example:

GraphQLModule.forRoot<ApolloDriverConfig>({
  driver: ApolloDriver,
})
    at Function.assertDriver (/Users/valentin/Perso/Projects/reproducible/node_modules/@nestjs/graphql/dist/graphql.module.js:121:19)
    at Function.forRoot (/Users/valentin/Perso/Projects/reproducible/node_modules/@nestjs/graphql/dist/graphql.module.js:36:14)
    at Object.<anonymous> (/Users/valentin/Perso/Projects/reproducible/src/app.module.ts:18:19)
    at Module._compile (node:internal/modules/cjs/loader:1103:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1157:10)
    at Module.load (node:internal/modules/cjs/loader:981:32)
    at Function.Module._load (node:internal/modules/cjs/loader:822:12)
    at Module.require (node:internal/modules/cjs/loader:1005:19)
    at require (node:internal/modules/cjs/helpers:102:18)
    at Object.<anonymous> (/Users/valentin/Perso/Projects/reproducible/src/main.ts:2:1)

The documentation would need to add

npm i @nestjs/apollo

and

import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';

// ...

    GraphQLModule.forRoot<ApolloDriverConfig>({
      // set to true to automatically generate schema
      autoSchemaFile: true,
      driver: ApolloDriver,
    }),

Have you read the Contributing Guidelines on issues?

Yes

Exposing pubSub through services and resolvers

It would be absolutely fantastic if the underlying CRUDResolver and matching ORM service could expose the pubSub api nicely. Right now its hidden and hard to use in a custom fashion, it also will add JSON.stringify(authorizationFilter) to the publish event name sometimes, not entirely sure how or why this is?

e.g. (pseudo-code)

In resolver:

@Resolver(() => SomeDto)
export class SomeDtoResolver extends CRUDResolver(SomeDto, {
  enableSubscriptions: true,
}) {
  constructor(
    private readonly someService: SomeService,
    @InjectPubSub() override readonly pubSub: PubSub,
  )

  @Mutation(() => Boolean)
  async customUpdate(@Args() { id, update }: UpdateVariables) {
    const updated = await this.update(id, update);
    await this.publishUpdatedOneEvent(updated);
  }
}

It would also be great if there was way to publish events easily from services too, so that custom business logic outside the crud resolvers can still hook into publishing updated events

ER_NO_DEFAULT_FOR_FIELD: Field 'userId' doesn't have a default value

Describe the bug
I have a Relation between a User and a AgentRequest. When creating a AgentRequest i get always the "userId" doesn't have default value error.

User

@Entity()
export class UserEntity{
  @PrimaryColumn()
  id!: string;

...

  @OneToMany(() => AgentRequestEntity, (request) => request.user)
  requests ?: AgentRequestEntity[];
  //================================================
  // Timestamp
  //================================================
  @CreateDateColumn()
  createdAt?: Date;
    
  @UpdateDateColumn()
  updatedAt?: Date;
    
  @DeleteDateColumn()
  deletedAt?: Date;
}
@ObjectType("User")
@FilterableCursorConnection('requests', () => AgentRequestDTO, { disableRemove: true })
export class UserDTO {
    @FilterableField()
    id!: string

    @FilterableField(() => GraphQLISODateTime)
    createdAt: Date
  
    @FilterableField(() => GraphQLISODateTime)
    updatedAt: Date
  
    @FilterableField(() => GraphQLISODateTime)
    deletedAt: Date
}
@Module({
  providers: [UserResolver],
  imports: [
    NestjsQueryGraphQLModule.forFeature({
      imports: [NestjsQueryTypeOrmModule.forFeature([UserEntity])],
      assemblers: [UserAssembler],
      resolvers: [{
        DTOClass: UserDTO, 
        EntityClass: UserEntity,
        UpdateDTOClass: UpdateUserInputDTO,
        create: {disabled: true},
        delete: {disabled: true},
        read: {disabled: true}
      }],
    })
  ],
})
export class UsersModule {}

Agent Request

@Entity()
export class AgentRequestEntity{

    @PrimaryGeneratedColumn()
    id !: number;

    @ManyToOne(type => UserEntity, user => user.requests, {nullable: false})
    user !: UserEntity;

    // timestamp
    @CreateDateColumn()
    createdDate !: Date;
     
    @UpdateDateColumn()
    updatedDate !: Date;
     
    @DeleteDateColumn()
    deletedDate ?: Date;
}
@Relation('user', () => UserDTO, { disableRemove: true })
@ObjectType('AgentRequest')
export class AgentRequestDTO {
  @FilterableField(() => ID)
  id!: number;

  @FilterableField(() => Date)
  createdDate!: Date;

  @FilterableField(() => Date)
  updatedDate!: Date;

  @FilterableField(() => Date)
  deletedDate ?: Date;

  @FilterableField()
  userId !: string;
}
@Module({
  imports: [
    TypeOrmModule.forFeature([AgentRequestEntity, UserEntity]),
    NestjsQueryGraphQLModule.forFeature({
      imports: [NestjsQueryTypeOrmModule.forFeature([AgentRequestEntity])],
      dtos: [{DTOClass: AgentRequestDTO}],
      resolvers: [{
        DTOClass: AgentRequestDTO, 
        EntityClass: AgentRequestEntity, 
      }], 
    })
  ],
  providers: [  
    AuthModule
  ],
})
export class RequestModule {}

App Module

@Module({
  imports: [
    GraphQLModule.forRoot({
      include: [
        UsersModule, 
        AgentModule, 
        RequestModule, 
        AddressModule
      ],
      driver: ApolloDriver,
      context: (context) => {
        return context;
      },
      autoSchemaFile: 'schema.gql',
      transformSchema: schema => upperDirectiveTransformer(schema, 'upper'),
      installSubscriptionHandlers: true,
      useGlobalPrefix: true,
      csrfPrevention: true,
      buildSchemaOptions: {
        directives: [
          new GraphQLDirective({
            name: 'upper',
            locations: [DirectiveLocation.FIELD_DEFINITION],
          }),
        ],
      },
    }),
    TypeOrmModule.forRoot({
      type:     process.env.USERS_DB_TYPE,
      port:     process.env.USERS_DB_PORT,
      host:     process.env.USERS_DB_HOST,
      username: process.env.USERS_DB_USER,
      password: process.env.USERS_DB_PW, 
      database: process.env.USERS_DB_NAME,
      entities: [
        UserEntity, 
        AddressEntity, 
        AgentEntity, 
        AgentRequestEntity
      ], 
      synchronize: true,
      autoLoadEntities : true,
    }),
    KeycloakConnectModule.register({
      authServerUrl: 'xxx',
      realm: 'xxx',
      clientId: 'xxx',
      secret: xxx,   
      policyEnforcement: PolicyEnforcementMode.PERMISSIVE, // optional
      tokenValidation: TokenValidation.ONLINE, // optional
    }),
    AddressModule,
    UsersModule,
    RequestModule,
    AgentModule
  ],
  providers:   [
    {
      provide: APP_GUARD,     
      useClass: AuthGuard,
    },
    {
      provide: APP_GUARD,
      useClass: ResourceGuard,
    },
    {
      provide: APP_GUARD,
      useClass: RoleGuard,
    }
  ],
})
export class AppModule{}

Have you read the Contributing Guidelines?

yes

To Reproduce
Steps to reproduce the behavior:
1.

mutation MyMutation {
  createOneAgentRequest(
    input: {agentRequest: {userId: "5cc229b0-c207-4075-b2c0-d8a684e085a4"}}
  ) {
    id
  }
}

Expected behavior
Create a AgentRequest.

Screenshots
image
image

Desktop (please complete the following information):
"@ptc-org/nestjs-query-core": "^1.1.0",
"@ptc-org/nestjs-query-graphql": "^1.1.0",
"@ptc-org/nestjs-query-typeorm": "^1.1.0",
FROM node:18-alpine

Additional context
Add any other context about the problem here.

Add an extra query/parameter to fetch without pagination

Is your feature request related to a problem? Please describe.

Most of the time I was to fetch my data in a paginated way, so I'm don't want to use

@QueryOptions({ pagingStrategy: PagingStrategies.NONE })

But in some specific scenarios, I don't want to have paginated data but I want to fetch the entire list.
I cannot reuse the generated queries as they require paging.first to be not null and >1

For example, the generated query would be:

type Query {
  users(
    """Limit or page results."""
    paging: CursorPaging = {first: 10}

    """Specify to filter the records returned."""
    filter: UserFilter = {}

    """Specify to sort results."""
    sorting: [UserSort!] = []
  )
}

input CursorPaging {
  """Paginate before opaque cursor"""
  before: ConnectionCursor

  """Paginate after opaque cursor"""
  after: ConnectionCursor

  """Paginate first"""
  first: Int

  """Paginate last"""
  last: Int
}

Because of that, I have to create a custom @Query.

Have you read the Contributing Guidelines?

Yes

Describe the solution you'd like

It would be nice to create an un-paginated query or support paging.first being null (or whatever other solution) to fetch the unpaginated list of records.

Describe alternatives you've considered

I'm creating a new query end point by hand.

Passing Graphql query resolver args as part of call to QueryService

Is it possible to get the parent and info resolver args from the CRUD resolver without too much hassle or code rewrite?

The particular case here, we're hoping from a means of passing parent and info resolver args in the CRUD DTOs, beause this will allow users to dynamically create the query

How to use ConnectionName with Mongoose ?

In documentation multiple databases section with TypeOrm is provided . However MongooseModule.forRootAsync also supports
multiple connections by default . So is it not supported in nestjs-query ? or is there any solution for this ?

Ability to fetch the last page

// Required `before`. This is a weird corner case.
// We'd have to invert the ordering of query to get the last few items then re-invert it when emitting the results.
// We'll just ignore it for now.
@Validate(CannotUseWithout, ['before'])
@Validate(CannotUseWith, ['after', 'first'])
@Min(1)
@IsPositive()
last?: number

Right now, we cannot use last without before so if we don't know the cursor of the last item, we cannot fetch the last page.

Typegoose enhancements

Thanks for making a fork of this project! It's too good not to continue.

I have a couple things layered on top of nestjs-query which enhances typegoose experience:

  • subdocument query
  • subdocument filters
  • custom relations (subdocument relations, better data loader etc.)

I will try to make a PR or make a separate package that can be used on top of nestjs-query when I find the time

Cheers!

Support fallback column for sorting when the value is NULL

Is your feature request related to a problem? Please describe.

I have a list of tasks that is sorted and paginated.
Each task can have a plannedStartDate and an actualStartDate.

I show the tasks in a table with a column 'Start date'

  • if the task has an actualStartDate it displays the actualStartDate
  • if actualStartDate is null, it displays the plannedStartDate

I would like to sort my list with nestjs-query on the 'Start Date' equivalent to this SQL:

ORDER BY CASE 
    WHEN actualStartDate IS NOT NULL THEN actualStartDate
    ELSE plannedStartDate
END

If I put the columns actualStartDate and plannedStartDate in the SortField list, it will sort the tasks by actualStartDate first and then by plannedStartDate, but it will not 'mix' the two and sort the tasks on this combined column.

Have you read the Contributing Guidelines?

Yes

Describe alternatives you've considered
For the moment the only solution I see it to use a custom SQL query instead of nestjs-query.

Nested filtering doesn't work

For one of my responses I've created @FilterableUnPagedRelation('trackUsers')

But when I tried filter by userId from that relation

{
getTracks(filter: {
trackUsers: {
userId: { eq: "some uuid" }
}
}) {
nodes {
id
}
}
}

I received:
"Error: unknown operator "userId"",
" at SQLComparisonBuilder.build (C:\projects_unitea\unitea-backend-ms-media\packages\query-typeorm\src\query\sql-comparison.builder.ts:90:11)",

It it like library try to parse filterabled field as operator.

Prisma Integration

Prisma is an open source next-generation ORM. Currently, it is not officially integrated with the nestjs-query package. It would be nice and easier for developer to use if we could get some official integration.

Have you read the Contributing Guidelines?

Yes

Describe the solution you'd like
Integration for Prisma ORM

Additional context
Ofcourse I don't expect full integration in one night. But a small start would be very nice. I'm not know much thing yet, but I'm happy to help if u need it.

OnetoMany relationships not returning results, blocked in graphql

After migration to TriPSs I have identified that all of my @OneToMany relationships on the single "has many" side return empty arrays instead of results. Interestingly, aggregate results on this relationship still appear to work. Here is an example where a location should have n actions and you can see the actionAggregate has 1, but the results are empty.

{
  "data": {
    "locations": {
      "edges": [
        {
          "node": {
            "id": "us-minnesota-sauk rapids",
            "actionsAggregate": [
              {
                "count": {
                  "id": 1
                }
              }
            ],
            "actions": {
              "edges": []
            }
          }
        },
<--snip-->

I've confirmed this is not a typeorm issue. using the following,

  @Query(() => [Location])
  async test() {
    const results = await this.locationRepository.find({
      where: { id: 'us-minnesota-sauk rapids' },
      relations: {
        actions: true,
      },
    });
    console.log(results);
    return results;
  }
[
  Location {
    isActive: true,
    isArchived: false,
    internalComment: null,
    created: 2023-03-21T15:50:04.902Z,
    createdBy: null,
    updated: 2023-03-21T15:50:04.902Z,
    updatedBy: null,
    deletedOn: null,
    deletedBy: null,
    id: 'us-minnesota-sauk rapids',
    city: 'Sauk Rapids',
    province: 'Minnesota',
    country: 'US',
    timezone: 'America/Chicago',
    region: 'NA',
    actions: [ [Action] ]
  }
]

yet the returned results are still incorrect:

{
  "data": {
    "test": [
      {
        "id": "us-minnesota-sauk rapids",
        "actions": {
          "edges": []
        }
      }
    ]
  }
}

which leads me to believe the graphql declaration on the model.. which looks like this:

@Entity()
@ObjectType()
@QueryOptions({ defaultResultSize: DEFAULT_QUERY_RESULTS, maxResultsSize: -1 })
@CursorConnection('articles', () => Article, {
  nullable: true,
  disableRemove: false,
})
@CursorConnection('actions', () => Action, {
  nullable: true,
  disableRemove: false,
})
export class Location extends CommonFields {
  @IDField(() => ID)
  @PrimaryColumn()
  id: string;

  @FilterableField({ nullable: true })
  @Column({ nullable: false })
  city?: string;

  @FilterableField({ nullable: true })
  @Column({ nullable: true })
  province?: string;

  @FilterableField({ nullable: true })
  @Column({ nullable: true })
  country!: string;

  @Field({ nullable: true })
  @Column({ nullable: true })
  timezone?: string;

  @FilterableField({ nullable: true })
  @Column({ nullable: true })
  region?: string;

  @OneToMany(() => Article, (article) => article.location)
  articles: Article[];

  @OneToMany(() => Action, (action) => action.location)
  actions: Action[];
}

I'm not sure if this is a nestjs issue or not.. For reference, here are my relevant packages:

    "@nestjs/apollo": "11.0.0",
    "@nestjs/axios": "^2.0.0",
    "@nestjs/common": "9.3.9",
    "@nestjs/config": "^2.3.1",
    "@nestjs/core": "9.3.9",
    "@nestjs/graphql": "11.0.0",
    "@nestjs/jwt": "^10.0.2",
    "@nestjs/passport": "^9.0.3",
    "@nestjs/platform-express": "9.3.9",
    "@nestjs/sequelize": "^9.0.0",
    "@nestjs/typeorm": "^9.0.1",
    "@ptc-org/nestjs-query-core": "^2.3.0",
    "@ptc-org/nestjs-query-graphql": "^2.3.0",
    "@ptc-org/nestjs-query-sequelize": "^2.3.0",
    "@ptc-org/nestjs-query-typeorm": "^2.3.0",

I should mention that I have other OneToMany examples which are annotated as FilterableUnpagedRelation and UnpagedRelation that also fail to return results and only return empty arrays.

Any help or guidance would be greatly appreciated

How can I make it work on entities with tree hierarchy using the materialized path pattern

Hi I'm using mongoose-mpath package. How can I make nestjs-query work with it?

Category schema:

@Schema({
  timestamps: true,
  collection: 'categories',
})
export class Category extends MaterializedPathSchema<Category> {
  _id: MongooseSchema.Types.ObjectId;

  slug: string;

  @Prop({
    required: true,
    type: CreateCategoryTranslationsInputDTO,
  })
  translations: CreateCategoryTranslationsInputDTO;

  @Prop()
  createdAt: Date;

  @Prop()
  updatedAt: Date;
}

export const CategorySchema = SchemaFactory.createForClass(Category);
CategorySchema.plugin(mongooseSlugPlugin, {
  tmpl: '<%=translations.en.name%>',
  slugOptions: { "'": '' },
});
CategorySchema.plugin(MpathPlugin, { modelName: 'Category' });
CategorySchema.virtual('firstLevelChildren', {
  ref: 'Category',
  localField: '_id',
  foreignField: 'parent',
});

MaterializedPathSchema class:

export default abstract class MaterializedPathSchema<
  T extends InstanceType<SchemaType>,
> extends BaseSchema<T> {
  path: string;

  parent: T;

  children: T[];

  level: number;
}

Category DTO:

@ObjectType('Category')
@Relation('parent', () => CategoryDTO, {
  nullable: true,
})
@CursorConnection('children', () => CategoryDTO, {
  relationName: 'firstLevelChildren',
  pagingStrategy: PagingStrategies.NONE,
  disableRemove: true,
  disableUpdate: true,
})
export class CategoryDTO {
  @IDField(() => ID)
  @ObjectId()
  _id: MongooseSchema.Types.ObjectId;

  @FilterableField(() => String, { description: 'Category slug' })
  slug: string;

  @Field(() => CreateCategoryTranslationsInputDTO, {
    description: 'Category translations',
  })
  translations: CreateCategoryTranslationsInputDTO;
}

Currently, I can see "parent" on graphql schema with these code but I can't see "path", "children" and "level". Also, I don't think that's the right way to make parent show on the graphql schema

Version incompatibility with NestJS

Describe the bug

Hi @TriPSs,

First, I want to say thank you for continuing to maintain this project.

My issue is that I can't find a consistent set of versions to get it to install correctly. I run into an impossible case.

My existing project is using the newest version of Nest ("@nestjs/graphql": "^10.0.0")

But this package (your fork of nestjs-query) says that it requires

"@nestjs/graphql": "^9.0.0"

which itself requires

"graphql": "^15.0.0"

(instead of graphql 16 which is what the newest version of Nest uses).

So I tried to downgrade all my Nest packages to the following versions:

    "@nestjs/apollo": "^9.0.0",
    "@nestjs/common": "^8.0.0",
    "@nestjs/core": "^8.0.0",
    "@nestjs/graphql": "^9.0.0",
    "@nestjs/passport": "^8.0.0",
    "@nestjs/platform-express": "^8.0.0",
    "@nestjs/typeorm": "^8.0.1",
    "@ptc-org/nestjs-query-graphql": "^1.0.1",
    "@ptc-org/nestjs-query-sequelize": "^1.0.1",
    "apollo-server-express": "^3.10.1",
    "class-transformer": "^0.5.1",
    "graphql": "^15.0.0",

But then I get the error that
@apollo/[email protected]
which is used by your package:
@ptc-org/nestjs-query-graphql@"^1.0.1"
requires
graphql@"^16.5.0"

But... I can't use graphql 16 (see my comment above about having to use 15).

So I'm not sure where to go to get this working, since I seem to be stuck in a circle.

Do you have a stable set of package versions from your personal project(s) that you could share so that I can get this up and running?

Have you read the Contributing Guidelines?

Yep!

To Reproduce
Install the packages mentioned above and note the peer dependency issues.

Expected behavior
I expected that when downgrading NestJS to use the peer dependencies required by this project (i.e. "graphql": "^15.0.0" ) that then the other peer dependency of @apollo/[email protected] wouldn't complain about needing graphql 16.

Desktop (please complete the following information):

  • Node Version: 16.17.0
  • Npm: 15.8.0
  • Nestjs-query Version: @ptc-org/nestjs-query-graphql@"^1.0.1"

Additional context
I tried the --legacy-peer-deps options from npm but encountered errors when trying to run nest, so that short-term workaround doesn't seem possible.

OnBefore and UpdateDateColumn on base classes no longer fire

Describe the bug
This recently worked while using the Doug Martin code base. After migrating to TriPSs, it no longer does. I have a base class with updated:DateTime annotated with UpdateDateColumn, and OnBefore attributes. When saving entities which extend the base class, these no longer fire.

Here is the base class:
`import { PrimaryGeneratedColumn, Column } from 'typeorm';
import { GraphQLISODateTime, ID, ObjectType } from '@nestjs/graphql';
import { FilterableField, IDField } from '@ptc-org/nestjs-query-graphql';
import { CommonFields } from './common-fields.entity';

@ObjectType()
export abstract class BaseEntity extends CommonFields {
@IDField(() => ID)
@PrimaryGeneratedColumn('uuid')
id: string;

@FilterableField(() => GraphQLISODateTime)
@column({ type: 'timestamp', nullable: true })
remoteUpdated?: Date;
}`

Here is the intermediate class:
`import {
Column,
UpdateDateColumn,
CreateDateColumn,
Index,
DeleteDateColumn,
} from 'typeorm';
import { Field, GraphQLISODateTime, ObjectType } from '@nestjs/graphql';
import {
BeforeCreateMany,
BeforeCreateOne,
BeforeUpdateMany,
BeforeUpdateOne,
CreateManyInputType,
CreateOneInputType,
FilterableField,
UpdateManyInputType,
UpdateOneInputType,
} from '@ptc-org/nestjs-query-graphql';
import { UserContextOnRequest } from '../../auth/user-context.interface';

@ObjectType()
@BeforeCreateOne(
(input: CreateOneInputType, context: UserContextOnRequest) => {
console.log('BeforeCreateOne');
// eslint-disable-next-line no-param-reassign
const createdBy = context.req.user?.userContext
? context.req.user?.userContext.email
: 'Unknown';
return {
input: { ...input.input, createdBy: createdBy, updatedBy: createdBy },
};
},
)
@BeforeCreateMany(
(input: CreateManyInputType, context: UserContextOnRequest) => {
console.log('BeforeCreateMany');
const createdBy = context.req.user?.userContext
? context.req.user?.userContext.email
: 'Unknown';
// eslint-disable-next-line no-param-reassign
input.input = input.input.map((c) => ({
...c,
createdBy,
updatedBy: createdBy,
}));
return input;
},
)
@BeforeUpdateOne(
(input: UpdateOneInputType, context: UserContextOnRequest) => {
console.log('BeforeUpdateOne');
// eslint-disable-next-line no-param-reassign
const updatedBy = context.req.user?.userContext
? context.req.user?.userContext.email
: 'Unknown';
input.update.updatedBy = updatedBy;
return input;
},
)
@BeforeUpdateMany(
(
input: UpdateManyInputType<CommonFields, CommonFields>,
context: UserContextOnRequest,
) => {
console.log('BeforeUpdateMany');
const updatedBy = context.req.user?.userContext
? context.req.user?.userContext.email
: 'Unknown';
// eslint-disable-next-line no-param-reassign
input.update.updatedBy = updatedBy;
return input;
},
)
export abstract class CommonFields {
@FilterableField({ defaultValue: true })
@column({ type: 'boolean', default: true })
isActive: boolean;

@FilterableField({ defaultValue: false })
@column({ type: 'boolean', default: false })
isArchived: boolean;

@FilterableField({ nullable: true })
@column({ type: 'varchar', nullable: true })
@Index({ fulltext: true })
internalComment?: string;

@FilterableField(() => GraphQLISODateTime)
@CreateDateColumn({ type: 'timestamp' })
created: Date;

@FilterableField({ nullable: true })
@column({ type: 'varchar', nullable: true })
@Index()
createdBy: string;

@FilterableField(() => GraphQLISODateTime)
@UpdateDateColumn({ type: 'timestamp' })
updated: Date;

@FilterableField({ nullable: true })
@column({ type: 'varchar', nullable: true })
updatedBy: string;

@field(() => GraphQLISODateTime, { nullable: true })
@DeleteDateColumn({ type: 'timestamp', nullable: true })
deletedOn?: Date;

@FilterableField({ nullable: true })
@column({ type: 'varchar', nullable: true })
deletedBy: string;
}
`

here is the end class:
`import { FilterableField, QueryOptions } from '@ptc-org/nestjs-query-graphql';
import { Field, ObjectType, registerEnumType } from '@nestjs/graphql';
import { DEFAULT_QUERY_RESULTS } from '../config/constants';
import { Entity, Column, Index } from 'typeorm';
import { BaseEntity } from './base/base.entity';

export enum ActivityLogType {
EMAIL_DAILY = 'EMAIL-DAILY',
EMAIL_SUB_CHECK = 'EMAIL-SUB-CHECK',
SYNC_AGENT_PP = 'SYNC-PP',
SEARCH = 'SEARCH',
MUTATION = 'MUTATION',
AUTHENTICATION = 'AUTHENTICATION',
QUERY = 'QUERY',
API = 'API',
EXCEL = 'EXCEL',
}

registerEnumType(ActivityLogType, {
name: 'ActivityLogType',
description: 'Activity Log Types',
});

@entity()
@ObjectType()
@QueryOptions({ defaultResultSize: DEFAULT_QUERY_RESULTS, maxResultsSize: -1 })
export class ActivityLog extends BaseEntity {
@FilterableField(() => ActivityLogType, { nullable: false })
@column({ nullable: false })
@Index()
type!: ActivityLogType;

@field({ nullable: true })
@column({ nullable: true })
details?: string;

@FilterableField({ nullable: true })
@column({ nullable: true })
@Index()
referenceEntity?: string;

@FilterableField({ nullable: true })
@column({ nullable: true })
@Index()
referenceId?: string;
}
`

and now when I attempt a createOneEntity for an Entity based on this, the return error is:
[GraphQL error]: Message: Variable "$input" got invalid value { type: "EMAIL_DAILY", details: "" } at "input.activityLog"; Field "created" of required type "DateTime!" was not provided., Location: [object Object], Path: undefined [GraphQL error]: Message: Variable "$input" got invalid value { type: "EMAIL_DAILY", details: "" } at "input.activityLog"; Field "id" of required type "ID!" was not provided., Location: [object Object], Path: undefined [GraphQL error]: Message: Variable "$input" got invalid value { type: "EMAIL_DAILY", details: "" } at "input.activityLog"; Field "remoteUpdated" of required type "DateTime!" was not provided., Location: [object Object], Path: undefined [GraphQL error]: Message: Variable "$input" got invalid value { type: "EMAIL_DAILY", details: "" } at "input.activityLog"; Field "updated" of required type "DateTime!" was not provided., Location: [object Object], Path: undefined

Expected behavior
when saving the final entity, the default values or set values from annotations in ancestor classes fire as required to set the values. Again, this use to work with Doug Martin's version of the framework

Any ideas?

Support TypeORM VirtualColumn

Is your feature request related to a problem? Please describe.
A new @VirtualColumn decorator was recently added to TypeORM as a read-only virtual column that is generated with each find. It supports WHERE and ORDER BY filtering and sorting, however, nestjs-query does not seem to be generating the proper query code (straight SQL instead of TypeORM repository query)?

I end up with the following GraphQL error:

"errors": [
    {
      "message": "column EventEntity.is_active does not exist",
. . .

I am able to successfully query the Field of the VirtualColumn, but as soon as I try to filter or sort, it says the column doesn't exist (it really doesn't, but the repository should handle that).

TypeORM PR for VirtualColumn feature:
typeorm/typeorm#9339

Have you read the Contributing Guidelines?

Yes

Describe the solution you'd like
Filtering and Sorting on a Virtual Column should work.

Describe alternatives you've considered
Getter/generated field, which does not allow filtering or sorting.

Some kind of cache was introduced in [10.1.7](https://github.com/nestjs/graphql/releases/tag/v10.1.7) which makes the graphql tests fail as they are no longer independent.

Some kind of cache was introduced in 10.1.7 which makes the graphql tests fail as they are no longer independent.
The simplest example I found is here:

it('should use the object type name', () => expectResolverSDL({ one: { relation: { DTO: TestRelationDTO } } }))
it('should use the dtoName if provided', () =>
expectResolverSDL({ one: { relation: { DTO: TestRelationDTO, dtoName: 'Test' } } }))

If you run both of these tests one by one, they pass. However, together, the second will fail because the TestResolverDTO will still have the default relation that was created in the first one.

If you want, I could also pin it to 10.1.6.

Next, I'd like to work on upgrading to graphql 11, because that's what is required to run the dev tools and I would really like that.

Originally posted by @GP4cK in #118 (comment)

Created this ticket so we can check this out and others can see why potentially some of their tests are failing of they update this dep.

[filterQueryBuilder] TS Compilation error

Hi,
I am not sure if I have to set something in tsconfig but when Nest.js is trying to compile there is a typescript error I guess:

/usr/src/app/node_modules/@ptc-org/nestjs-query-typeorm/src/services/typeorm-query.service.js:27
        this.filterQueryBuilder = opts?.filterQueryBuilder ?? new query_1.FilterQueryBuilder(this.repo);
                                       ^

SyntaxError: Unexpected token '.'
    at wrapSafe (internal/modules/cjs/loader.js:915:16)
    at Module._compile (internal/modules/cjs/loader.js:963:27)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)
    at Module.load (internal/modules/cjs/loader.js:863:32)
    at Function.Module._load (internal/modules/cjs/loader.js:708:14)
    at Module.require (internal/modules/cjs/loader.js:887:19)
    at require (internal/modules/cjs/helpers.js:74:18)
    at Object.<anonymous> (/usr/src/app/packages/query-typeorm/src/services/index.ts:1:1)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)

Support for mongoose v6

Have you read the Contributing Guidelines?

Describe the solution you'd like
Upgrade the peerDependency from mongoose v5 to v6. I've tested it with the majority of features and it seems to be working as-expected.
Describe alternatives you've considered
It uses the mongoDB driver v4, so that is something to consider. Looking at the breaking changes I don't think we use much of what's there: https://mongoosejs.com/docs/migrating_to_6.html

If we're comfortable moving the project to v6, I'm happy to take it on.

Authorization improvement

Im trying to build an authorization system using nestjs-query but I found a limitation.
e.g: Im trying to check if a user has access to some products.
Say you have a query like this:

query getProducts {
  products(filter: { id: { in: [1,2,3] } }) {
    id
  }
}

but the user only has access to product 1,2 so I want to call a service to check this but I am not able to read query / mutation args because AuthorizationFilter is sending the ExecutionContext, not the GqlExecutionContext.
I think the issues is here: https://github.com/doug-martin/nestjs-query/blob/v0.27.0/packages/query-graphql/src/decorators/authorize-filter.decorator.ts#L10

Unfortunately I am still using v0.27 and there is a long way to go untill I can upgrade to v0.30. Meanwhile I will use Guards to solve this issue.

Delete subscription not working

Describe the bug
A clear and concise description of what the bug is.

Have you read the Contributing Guidelines?

Yes

To Reproduce
Steps to reproduce the behavior:

  1. Enable subscriptions in resolver
  2. Subscribe to deletedOne or deletedMany
  3. Trigger a deleteOne or deleteMany mutation

Expected behavior
The subscription should trigger if an object was deleted.

Screenshots
n/a

Desktop (please complete the following information):

  • Node Version: 14.14.0
  • Nestjs-query Version: 1.1.2

Additional context
The other subscriptions, like updated and created are working fine, but deleted not.

Mercurius support

Is your feature request related to a problem? Please describe.
As you may now, nestjs graphql support both apollo and mercurius as a graphql server.
Do you plan to support both ?

I've made some tests and the nestjs-query pretty much already support it.
The only thing that would require some modification is the graphql definition.

Have you read the Contributing Guidelines?

yes :)

Describe the solution you'd like

The query & mutations works out of the box pretty much. I did some test on the original package, not on your fork.
Only the subscription definition is a bit different on a mercurius server.

On the plus side.

  • Mercurius is a lot more performant.
  • you can use subscriptions on a federated gql server

Additional context

I would be glade to help has this package make me gain a lot of time in setting up projects.

Let me know what you thing

Extend nested filter input-types to allow filter-depth configuration per relation.

Extend nested filter input-types (#143) to allow filter-depth configuration per relation.

@FilterableRelation('relation', () => Relation, {
  // This one does not work yet
  filterDepth: 2,
})

Describe the solution you'd like

Requires further customization in the /nestjs-query/packages/query-graphql/src/types/query/filter.type.ts file. At Object.keys(filterableRelations).forEach((field) => { the filterDepth of the relation should be considered (only if set), not the filterDepth of the parent element.

// Example-A.entity.ts
@ObjectType('EntityA')
@FilterableRelation('relation', () => relation)
class EntityA { /* ... */ }

// leads to
// input EntityAFilter {
//    # should generate depending on the `filterDepth` of `EntityA`
//    relation: EntityAFilterRelationFilter
// }
// Example-B.entity.ts
@ObjectType('EntityB')
@FilterableRelation('relation', () => relation, { filterDepth: 2 )
class EntityB { /* ... */ }

// leads to
// input EntityBFilter {
//     # should generate a filter type of depth 2
//     relation: EntityBFilterRelationFilter
// }

This is not a critical feature for me personally, so I will look at this in the future when I have more resources available again.

The instructions to run the documentation locally are wrong

๐Ÿ“š Documentation

In CONTRIBUTING.md, it says to run

cd ./documentation
npm run install # first time only
npm run start

to run the doc locally, but this is not working. There is no package.json file, and, for example, running npm run start fails:

npm ERR! Missing script: "start"
npm ERR! 
npm ERR! Did you mean one of these?
npm ERR!     npm star # Mark your favorite packages
npm ERR!     npm stars # View packages marked as favorites
npm ERR! 
npm ERR! To see a list of scripts, run:
npm ERR!   npm run

It should be replaced by

cd ./documentation
npx docusaurus start

### Serving Docs Locally
Running the documentation locally is the easiest way to view your changes to ensure they render as you expect.
To run the docs locally do the following:
```
cd ./documentation
npm run install # first time only
npm run start
```
### Creating A New Page

Have you read the Contributing Guidelines on issues?

Yes

Non-shareable fields coming from pagination

Describe the bug
I get the following errors when trying to create a federation graph:

Non-shareable field "PageInfo.hasNextPage" is resolved from multiple subgraphs: it is resolved from subgraphs "orders" and "shipments" and defined as non-shareable in all of them
Non-shareable field "PageInfo.hasPreviousPage" is resolved from multiple subgraphs: it is resolved from subgraphs "orders" and "shipments" and defined as non-shareable in all of them
Non-shareable field "PageInfo.startCursor" is resolved from multiple subgraphs: it is resolved from subgraphs "orders" and "shipments" and defined as non-shareable in all of them
Non-shareable field "PageInfo.endCursor" is resolved from multiple subgraphs: it is resolved from subgraphs "orders" and "shipments" and defined as non-shareable in all of them

Is there any way to add the directive to this type?

Have you read the Contributing Guidelines?

yes

Expected behavior
PageInfo type should have the @Shareable directive

@UnPagedRelation from the example does not work

Describe the bug
While following the example at https://tripss.github.io/nestjs-query/docs/graphql/relations#unpagedrelation

[Nest] 181764  - 03/03/2023, 2:43:08 AM   ERROR [ExceptionsHandler] Add operation is only supported for many-to-many and one-to-many relations. However given "subTasks" has many-to-one relation. Use .set() method instead.
TypeORMError: Add operation is only supported for many-to-many and one-to-many relations. However given "images" has many-to-one relation. Use .set() method instead.

The mutation is addSubTasksToTodoItem

I also have a setSubTasksOnTodoItem mutation, the error there is:

[Nest] 181764  - 03/03/2023, 2:51:24 AM   ERROR [ExceptionsHandler] Add operation is only supported for many-to-many and one-to-many relations. However given "images" has many-to-one relation. Use .set(null) method instead.
TypeORMError: Add operation is only supported for many-to-many and one-to-many relations. However given "images" has many-to-one relation. Use .set(null) method instead.

Have you read the Contributing Guidelines?

Yes

To Reproduce
Steps to reproduce the behavior:

  1. Following the example at https://tripss.github.io/nestjs-query/docs/graphql/relations#unpagedrelation
  2. Implement @UnPagedRelation

Expected behavior
To attach multiple SubTasks on TodoItem

  • Node Version v16.15.1
  • Nestjs-query Version [e.g. v0.21.0]
    "@nestjs/apollo": "10.2.0",
    "@nestjs/common": "9.3.9",
    "@nestjs/core": "9.3.9",
    "@nestjs/graphql": "10.2.0",
    "@nestjs/platform-express": "9.3.9",
    "@nestjs/typeorm": "9.0.1",
    "@ptc-org/nestjs-query-core": "2.2.0",
    "@ptc-org/nestjs-query-graphql": "2.2.0",
    "@ptc-org/nestjs-query-typeorm": "2.2.0",
    "apollo-server-express": "3.12.0",
    "class-transformer": "0.5.1",
    "class-validator": "0.14.0",
    "dataloader": "2.2.2",
    "graphql": "16.6.0",
    "graphql-subscriptions": "2.0.0",
    "pg": "8.9.0",
    "reflect-metadata": "0.1.13",
    "rxjs": "7.8.0",
    "typeorm": "0.3.12"

The inferred type of X cannot be named without a reference to '@ptc-org/nestjs-query-graphql/node_modules/@ptc-org/nestjs-query-core'. This is likely not portable. A type annotation is necessary

When extending a resolver to override some methods, I have the following error:
The inferred type of X cannot be named without a reference to '@ptc-org/nestjs-query-graphql/node_modules/@ptc-org/nestjs-query-core'. This is likely not portable. A type annotation is necessary

Here's a code sample:

@Resolver()
export class NotificationResolver extends ReadResolver(Notification, { one: { disabled: true } }) {
                ^^^^^^^^^^^^^^^^^^^ The inferred type of [...]
  constructor(
    private readonly notificationService: NotificationService,
  ) { super(notificationService); }
  // ...
}

To solve it, I had to add this to my tsconfig.json:

{
  "compilerOptions": {
    // Other stuff...
    "baseUrl": "./",
    "paths": {
      "@ptc-org/nestjs-query-graphql/node_modules/@ptc-org/nestjs-query-core": ["node_modules/@ptc-org/nestjs-query-core/"],
    },
  },
}

I believe there is some linking missing in the library but I don't know where to start looking to solve this issue...

Support for one-to-many query with TypeORM

Describe the bug
I was not able to get an array of items using a one-to-many query. I can clearly see that that query is being generated from TypeORM to fetch the items. But the result doesn't appear in the response.

To Reproduce
Following is the entity structure

@Entity({ name: 'operations' })
@ObjectType()
@UnPagedRelation('permissions', () => Permission, { disableRemove: true })
export class Operation {
  @IDField(() => ID)
  @PrimaryGeneratedColumn('increment')
  id!: string;

  @FilterableField()
  @Column({ nullable: false })
  name!: string;

  @OneToMany(() => Permission, (permission) => permission.operation)
  @JoinTable()
  permissions: Permission[];
}
@Relation('operation', () => Operation, {
  nullable: true,
  disableRemove: true,
})
@Entity({ name: 'permissions' })
@ObjectType()
export class Permission extends BaseEntity {
  @IDField(() => ID)
  @PrimaryGeneratedColumn('increment')
  id!: string;

  @FilterableField()
  @Column({ nullable: false })
  action!: string;

  @ManyToOne(() => Operation, (operation) => operation.permissions, {
    onDelete: 'CASCADE',
    nullable: false,
  })
  @JoinColumn({ name: 'operation_id' })
  public operation: Operation;
}

Body -

  operations {
    edges {
      node {
        id
        name
        permissions {
          id
          action
        }
      }
    }
  }

Error: Id cannot be specified when updating

Describe the bug
When I call mutation updateOneItem, I get this error Error: Id cannot be specified when updating. If I remove the id from the argument, graphQL produces this error Field \"id\" of required type \"ID!\" was not provided.

Module({
  imports: [
    NestjsQueryGraphQLModule.forFeature({
      imports: [NestjsQueryTypeOrmModule.forFeature([UserRole])],
      resolvers: [
        {
          DTOClass: UserRole,
          EntityClass: UserRole,
          UpdateDTOClass: UserRoleInput,
          create: {
            disabled: true,
          },
          update: {
            many: {
              disabled: true,
            },
          },
          delete: { disabled: true },
          pagingStrategy: PagingStrategies.NONE,
        },
      ],
    }),
  ],
  providers: [UserRoleService, UserRoleResolver],
  exports: [UserRoleService, UserRoleResolver],
})
export class UserRoleModule {}
type Mutation {
  updateOneUserRole(input: UpdateOneInput!): UserRole!
}

input UpdateOneInput {
  """The id of the record to update"""
  id: ID!

  """The update to apply."""
  update: UserRoleInput!
}

input UserRoleInput {
  deleted: Float
  id: ID
  name: String!
  description: String
  download: DownloadTypes!
  adminConsole: Float!
  manageSubscription: Float!
  createProject: Float = 0
}

Have you read the Contributing Guidelines?

Yes.

To Reproduce
Steps to reproduce the behavior:

  1. Step 1
  2. Step 2

Expected behavior
It updates successfully without error.

Screenshots
If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

  • Node Version [v16.1.3.0]
  • Nestjs-query Version [9.1.2]

Additional context
Add any other context about the problem here.

Async Assembler

Is your feature request related to a problem? Please describe.
I would like to fetch imageURLs before returning the DTO. (Assembler) I don't want to create a resolver, because i'm calling a different dto, which has a one-to-one relation to this dto.

query->HousingDTO <- OneToOne -> ListingDTO { imageURLs }

So doing it in the ListingDTO Assembler would be perfect. But the function to get the presignedURL from Minio is an async function.

How could I solve that?

Have you read the Contributing Guidelines?

yes

Describe the solution you'd like
Async Assembler, maybe the solution is very easy, sorry I'm not so familiar with JS.

Describe alternatives you've considered
Already tried to change the assembler function to async but then the Assembler Decorator is unhappy.

Additional context

@Assembler(UserDTO, UserEntity)
export class UserAssembler extends ClassTransformerAssembler<UserDTO, UserEntity> {
  
  convertToDTO(entity: UserEntity): UserDTO {
    const dto = super.convertToDTO(entity);
    
    dto.avatarPath = await getMediaURL("userPath");

    return dto;
  }
}

Support `CursorPaging.first` being `null` or add a extra parameter to fetch all.

This is kind of a follow-up of #62

Is your feature request related to a problem? Please describe.

As explained in #62 I want to use a paginated query most of the time. But in some specific/rare scenarios, I would like to fetch the entire list of items (and not only the first page).

Using 2 resolvers as suggested in #62 (comment) brings some issues because the type of the query changes between the paginated one (TodoConnection) and the unpaginated one (Todo).
For some languages/front-end stack (I'm using Flutter (dart) and ferry), the type differences are an issue and prevent abstractions.

That's why I would need to be able to reuse the same request/query for unpaginated and paginated lists.

Have you read the Contributing Guidelines?

Yes.

Describe the solution you'd like

Supporting and not specifying any CursorPaging.first or CusorPaging.last could return the entire list instead of just a page.
The returned type would still be TodoConnection, it's just that the edges will contain all the nodes.

Describe alternatives you've considered

Give an arbitrary very high value to first (ex: 1000)

The introduction example file names are not in sync when using nestjs 9

๐Ÿ“š Documentation

The cli of nest after running

npx nest g mo todo-item
npx nest g cl todo-item.entity todo-item
npx nest g cl todo-item.dto todo-item

generates

src/
  todo-item/
    todo-item.dto/
      todo-item.dto.ts
    todo-item.entity/
      todo-item.entity.ts
  todo-item.module.ts

and not

src/
  todo-item/
    todo-item.dto.ts
    todo-item.entity.ts
  todo-item.module.ts

anymore.

The name of the ts files and the imports are wrong in https://tripss.github.io/nestjs-query/docs/introduction/example

Have you read the Contributing Guidelines on issues?

Yes

Keycloak support

Is your feature request related to a problem? Please describe.
I can't use the nest-keycloak-connect decorators on my DTO classes.

Have you read the Contributing Guidelines?

yes

Describe the solution you'd like
Either support how to use the nest-keycloak-connect library with nestjs-query or how to write a authenticator for keycloak.

RelationQueryService query should be async

Hello guys,
One thing I think it would be useful is that the RelationQueryService should support async for virtual relations. At the moment you can't use async response for the query method.
e.g:


@QueryService(TodoItemReferenceDTO)
export class TodoItemService extends RelationQueryService<TodoItemReferenceDTO> {
  constructor(@InjectQueryService(SubTaskEntity) readonly subTaskService: QueryService<SubTaskEntity>) {
    super({
      // the name of the relation
      subTasks: {
        service: subTaskService,
        // a query factory that will take in the reference to create a query.
        async query: (todoItemReferenceDTO) => (
            // Do some database request or whatever
            return { filter: { todoItemId: { eq: todoItemReferenceDTO.id } } }
        ),
      },
    });
  }
}

Update docs for V3

๐Ÿ“š Documentation

Update the docs with the breaking changes for V3.

Lookahead `totalCount` and don't fetch if not requested

Is your feature request related to a problem? Please describe.
Some calls take additional time to count the amount of records even when the totalCount field is not fetched.

Have you read the Contributing Guidelines?

(Write your answer here.)

Describe the solution you'd like
Use #70 to lookahead and don't run the count query.

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Additional context
Now some queries take additional time to count the records even when the field is not retrieved causing additional latency.

Custom error class

Is your feature request related to a problem? Please describe.
Throw errors use the Error base class, it is difficult to process the caught errors differently (e.g.: with an exception filter)

Have you read the Contributing Guidelines?

Yes

Describe the solution you'd like
Use custom error classes
This would also allow adding a context (ex: I have two entities A and B with a many-to-many relation, I add several relations A to B where one is invalid, the custom error could contain the list of invalid identifiers)

@ptc-org/nestjs-query-sequelize package is missing some files.

Describe the bug
@ptc-org/nestjs-query-sequelize built package doesn't contain all the required files for it to function.

Error: Cannot find module './services'
Require stack:
- /<path to project>/server/node_modules/@ptc-org/nestjs-query-sequelize/src/index.js

To Reproduce
Steps to reproduce the behavior:

  1. Install @ptc-org/nestjs-query-sequelize package
  2. Include it.

"@ptc-org/nestjs-query-sequelize": "^1.0.0-alpha.1",

Name is not changed in the generated schema

Describe the bug
A clear and concise description of what the bug is.

Names don't get changed in Graphql schema, although name parameter was set in @FilterableField and @Field

To Reproduce
Here for example I change the name but it doesn't change

import { ArgsType, Field, Int, ObjectType } from '@nestjs/graphql';
import {
  FilterableField,
  QueryArgsType,
} from '@ptc-org/nestjs-query-graphql';

@ObjectType()
class MyObject:
  @FilterableField({ allowedComparisons: ['eq', 'like', 'notLike'], name: 'ip_address' })
  @Field({ name: 'ip_address', nullable: true })
  ipAddress?: string;


@ArgsType()
export class MyObjectQuery extends QueryArgsType(MyObject) { }

Expected behavior
name should be changed to ip_address but I get the typescript name ipAddress

Desktop (please complete the following information):

  • Node Version v16.13.2
    "@ptc-org/nestjs-query-core": "^1.1.4",
    "@ptc-org/nestjs-query-graphql": "^1.1.4",

Issue with types missing, possibly incorrectly published package???

I'm getting back to my project after several months and I'm bogged down with a couple of issues from nestjs-query-typegoose and to be honest, I'm not sure it's just my incompetence or what.

At any rate, this error is happening while building/ compiling:

src/user/user.module.ts(2,44): error TS7016: Could not find a declaration file for module '@ptc-org/nestjs-query-typegoose'. '/home/scott/Documents/tmp/m8a-new/common/temp/node_modules/.pnpm/@[email protected]_aefggeysizmixjxhkqtfgu72mu/node_modules/@ptc-org/nestjs-query-typegoose/src/index.js' implicitly has an 'any' type.
Try npm i --save-dev @types/ptc-org__nestjs-query-typegoose if it exists or add a new declaration (.d.ts) file containing declare module '@ptc-org/nestjs-query-typegoose';

This happens when I have strict: true set. If I set it to "off", the build goes through with no errors, but then I get this error while running my app:

Error: Cannot find module './module'
Require stack:

  • /home/scott/Documents/tmp/m8a-new/common/temp/node_modules/.pnpm/@[email protected]_aefggeysizmixjxhkqtfgu72mu/node_modules/@ptc-org/nestjs-query-typegoose/src/index.js
  • /home/scott/Documents/tmp/m8a-new/packages/core/dist/user/user.module.js
  • /home/scott/Documents/tmp/m8a-new/packages/core/dist/auth/auth.module.js
  • /home/scott/Documents/tmp/m8a-new/packages/core/dist/auth/index.js
  • /home/scott/Documents/tmp/m8a-new/packages/core/dist/index.js
  • /home/scott/Documents/tmp/m8a-new/packages/starter-kit-api/dist/app.module.js
  • /home/scott/Documents/tmp/m8a-new/packages/starter-kit-api/dist/main.js
    at Function.Module._resolveFilename (node:internal/modules/cjs/loader:955:15)
    at Function.Module._load (node:internal/modules/cjs/loader:803:27)
    at Module.require (node:internal/modules/cjs/loader:1021:19)
    at require (node:internal/modules/cjs/helpers:103:18)
    at Object. (/home/scott/Documents/tmp/m8a-new/common/temp/node_modules/.pnpm/@[email protected]_aefggeysizmixjxhkqtfgu72mu/node_modules/@ptc-org/nestjs-query-typegoose/src/index.js:4:16)
    at Module._compile (node:internal/modules/cjs/loader:1119:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1173:10)
    at Module.load (node:internal/modules/cjs/loader:997:32)
    at Function.Module._load (node:internal/modules/cjs/loader:838:12)
    at Module.require (node:internal/modules/cjs/loader:1021:19)

It's like the package wasn't built properly to begin with???

Have you read the Contributing Guidelines?

Yes

To Reproduce
Steps to reproduce the behavior:

Don't have any reproduction repo. Sorry. If it is a prerequisite, let me know.

Expected behavior
I'm expecting none of these errors.

Desktop (please complete the following information):

  • Node Version: 18.9.0
  • Nestjs-query Version: 1.1.0

I hope you can help me on this, as it's blocking any further work. Thanks!

Scott

How to do async convertion from Create DTO to Create Entity?

For example, I want to have an asynchronous process before converting a CreateDTO to a CreateEntity

import {
  Assembler,
  ClassTransformerAssembler,
  DeepPartial,
} from "@ptc-org/nestjs-query-core";
import {UserEntity} from "./user.entity";
import {UserInput} from "./user.input";

@Assembler(UserInput, UserEntity)
export class UserAssembler extends ClassTransformerAssembler<
  UserInput,
  UserEntity
> {
  private async convertInputToEntity(
    dto: DeepPartial<UserInput>,
    entity: DeepPartial<UserEntity>,
  ): Promise<void> {
    if (dto.password) {
      //~ Asynchronous process example
      entity.hashedPassword = await hashPassword(dto.password);
    }
  }

  override convertToCreateEntity(
    dto: DeepPartial<UserInput>,
  ): DeepPartial<UserEntity> {
    const entity = super.convertToCreateEntity(dto);
    await this.convertInputToEntity(dto, entity);
    return entity;
  }

  override convertToUpdateEntity(
    dto: DeepPartial<UserInput>,
  ): DeepPartial<UserEntity> {
    const entity = super.convertToUpdateEntity(dto);
    await this.convertInputToEntity(dto, entity);
    return entity;
  }
}

For sure this does not work because it's not an async function and there is no option for async process from the core functions.

Why don't all the interfaces use async functions by default? Is there any performance issue?

Shouldn't the generated `input CreateXxx` type have non-null field types when applicable?

I'm not sure whether this is a "bug" or if there are some edge cases I don't know about.

Describe the bug

I followed the the introduction getting started/install/example at https://tripss.github.io/nestjs-query/docs/introduction/example

From the TodoItemEntity :

@Entity()
export class TodoItemEntity {
  @PrimaryGeneratedColumn()
  id!: string;

  @Column()
  title!: string;

  @Column()
  completed!: boolean;

  @CreateDateColumn()
  created!: Date;

  @UpdateDateColumn()
  updated!: Date;
}

and TodoItemDTO:

@ObjectType('TodoItem')
export class TodoItemDTO {
  @IDField(() => ID)
  id!: number;

  @FilterableField()
  title!: string;

  @FilterableField()
  completed!: boolean;

  @Field(() => GraphQLISODateTime)
  created!: Date;

  @Field(() => GraphQLISODateTime)
  updated!: Date;
}

It generates the schema

type TodoItem {
  id: ID!
  title: String!
  completed: Boolean!
  created: DateTime!
  updated: DateTime!
}

which seems correct.

If I look at the input types for the updates:

input UpdateTodoItem {
  id: ID
  title: String
  completed: Boolean
  created: DateTime
  updated: DateTime
}

It makes sense for the update to only have nullable types. If title is null, it means it is not being updated.

However, for the creation input type:

input CreateTodoItem {
  id: ID
  title: String
  completed: Boolean
  created: DateTime
  updated: DateTime
}

Shouldn't title and completed be non-nullable as title and completed are not nullable?

For example, if I run

mutation {
  createOneTodoItem(
    input: { todoItem: { title: "Create One Todo Item"} }
  ) {
    id
    title
    completed
    created
    updated
  }
}

In the graphql playground, I get the error:

{
  "errors": [
    {
      "message": "null value in column \"completed\" violates not-null constraint",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "createOneTodoItem"
      ],
      "extensions": {
        "code": "INTERNAL_SERVER_ERROR",
        "exception": {
          "query": "INSERT INTO \"todo_item_entity\"(\"title\", \"completed\", \"created\", \"updated\") VALUES ($1, DEFAULT, DEFAULT, DEFAULT) RETURNING \"id\", \"created\", \"updated\"",
          "parameters": [
            "Create One Todo Item"
          ],
          "driverError": {
            "length": 272,
            "name": "error",
            "severity": "ERROR",
            "code": "23502",
            "detail": "Failing row contains (3, Create One Todo Item, null, 2023-01-13 06:31:35.856241, 2023-01-13 06:31:35.856241).",
            "schema": "public",
            "table": "todo_item_entity",
            "column": "completed",
            "file": "execMain.c",
            "line": "2042",
            "routine": "ExecConstraints"
          },
          "length": 272,
          "severity": "ERROR",
          "code": "23502",
          "detail": "Failing row contains (3, Create One Todo Item, null, 2023-01-13 06:31:35.856241, 2023-01-13 06:31:35.856241).",
          "schema": "public",
          "table": "todo_item_entity",
          "column": "completed",
          "file": "execMain.c",
          "line": "2042",
          "routine": "ExecConstraints",
          "stacktrace": [
            "QueryFailedError: null value in column \"completed\" violates not-null constraint",
            "    at PostgresQueryRunner.query (/Users/valentin/Perso/Projects/reproducible/src/driver/postgres/PostgresQueryRunner.ts:299:19)",
            "    at processTicksAndRejections (node:internal/process/task_queues:96:5)",
            "    at InsertQueryBuilder.execute (/Users/valentin/Perso/Projects/reproducible/src/query-builder/InsertQueryBuilder.ts:163:33)",
            "    at SubjectExecutor.executeInsertOperations (/Users/valentin/Perso/Projects/reproducible/src/persistence/SubjectExecutor.ts:428:42)",
            "    at SubjectExecutor.execute (/Users/valentin/Perso/Projects/reproducible/src/persistence/SubjectExecutor.ts:137:9)",
            "    at EntityPersistExecutor.execute (/Users/valentin/Perso/Projects/reproducible/src/persistence/EntityPersistExecutor.ts:197:21)",
            "    at DefaultAssembler.convertAsyncToDTO (/Users/valentin/Perso/Projects/reproducible/packages/core/src/assemblers/abstract.assembler.ts:64:15)",
            "    at AutoResolver.createOne (/Users/valentin/Perso/Projects/reproducible/packages/query-graphql/src/resolvers/create.resolver.ts:141:25)"
          ]
        }
      }
    }
  ],
  "data": null
}

which is a sql error. It would be better to catch is on the graphql level even before trying to run the sql query.

Have you read the Contributing Guidelines?

Yes

To Reproduce
Steps to reproduce the behavior:

  1. Follow the introduction part in https://tripss.github.io/nestjs-query/docs/introduction/example (you can also checkout https://github.com/ValentinVignal/reproducible/tree/ptc-org/nestjs-query/update-and-create-types)
  2. Run npm start

Expected behavior
A clear and concise description of what you expected to happen.

Screenshots
If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.