liberation-data / drivine

Best and fastest graph database client (Neo4j, AgensGraph, Apache AGE, FalkorDB) for Node.js & TypeScript.
https://drivine.org
Apache License 2.0
152 stars 33 forks source link

PersistenceManager:default not resolved - Nest.js version 8 #81

Closed magrinj closed 2 years ago

magrinj commented 3 years ago

Hi there, thanks for this great library, I'm trying to use it in my nest project in a leverage of Neo4j but I've some issues.

First I think the sample needs to be updated as they are importing stuff from nested path instead of @liberation-data/drivine and can lead in the same issue as #47. Same import is described in the user guide: https://drivine.org/guide/#/quick-start

After fixing this issue, I'm still facing an error with PersistenceManager:

[Nest] 5393  - 2021-08-21, 11:24:01 a.m.   ERROR [ExceptionHandler] Nest can't resolve dependencies of the UsersRepository (?, CYPHER:/[CENSORED]/dist/src/users/cyphers/create.cypher, CYPHER:/[CENSORED]/dist/src/users/cyphers/users.cypher, CYPHER:/[CENSORED]/dist/src/users/cyphers/userById.cypher). Please make sure that the argument PersistenceManager:default at index [0] is available in the UsersModule context.

Potential solutions:
- If PersistenceManager:default is a provider, is it part of the current UsersModule?
- If PersistenceManager:default is exported from a separate @Module, is that module imported within UsersModule?
  @Module({
    imports: [ /* the Module containing PersistenceManager:default */ ]
  })

Error: Nest can't resolve dependencies of the UsersRepository (?, CYPHER:/[CENSORED]/dist/src/users/cyphers/create.cypher, CYPHER:/[CENSORED]/dist/src/users/cyphers/users.cypher, CYPHER:/[CENSORED]/dist/src/users/cyphers/userById.cypher). Please make sure that the argument PersistenceManager:default at index [0] is available in the UsersModule context.

Potential solutions:
- If PersistenceManager:default is a provider, is it part of the current UsersModule?
- If PersistenceManager:default is exported from a separate @Module, is that module imported within UsersModule?
  @Module({
    imports: [ /* the Module containing PersistenceManager:default */ ]
  })

Here is a look on my files:

app.module.ts

 import { Module } from '@nestjs/common';
import {
  DatabaseRegistry,
  DrivineModule,
  DrivineModuleOptions,
} from '@liberation-data/drivine';
import { GraphQLModule } from '@nestjs/graphql';
import { ConfigModule } from '@nestjs/config';
import { join } from 'path';

import { CognitoModule } from 'src/cognito/cognito.module';
import { AuthModule } from 'src/auth/auth.module';
import { UsersModule } from 'src/users/users.module';

import { validate } from 'src/configuration/env.validation';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      validate,
    }),
    DrivineModule.withOptions(<DrivineModuleOptions>{
      connectionProviders: [DatabaseRegistry.buildOrResolveFromEnv('NEO4J')],
    }),
    CognitoModule.fromEnv(),
    GraphQLModule.forRoot({
      autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
      sortSchema: true,
      debug: process.env.NODE_ENV === 'development',
      playground: process.env.NODE_ENV === 'development',
    }),
    AuthModule,
    UsersModule,
  ],
})
export class AppModule {}

users.module.ts

 import { Module } from '@nestjs/common';

import { UsersService } from 'src/users/users.service';
import { UsersResolver } from 'src/users/users.resolver';
import { UsersRepository } from 'src/users/users.repository';

@Module({
  providers: [UsersService, UsersResolver, UsersRepository],
  exports: [UsersService],
})
export class UsersModule {}

users.repository.ts

 import { Injectable } from '@nestjs/common';
import {
  CypherStatement,
  InjectCypher,
  InjectPersistenceManager,
  PersistenceManager,
  QuerySpecification,
  Transactional,
} from '@liberation-data/drivine';
import { classToPlain } from 'class-transformer';

import { UserEntity } from 'src/users/entities/user.entity';

@Injectable()
export class UsersRepository {
  constructor(
    @InjectPersistenceManager() readonly persistenceManager: PersistenceManager,
    @InjectCypher(__dirname, 'cyphers/create')
    readonly createCypher: CypherStatement,
    @InjectCypher(__dirname, 'cyphers/users')
    readonly usersCypher: CypherStatement,
    @InjectCypher(__dirname, 'cyphers/userById')
    readonly userByIdCypher: CypherStatement,
  ) {}

  @Transactional()
  async create(user: UserEntity) {
    const spec = new QuerySpecification<UserEntity>()
      .withStatement(this.createCypher)
      .bind([classToPlain(user)]);

    return this.persistenceManager.query(spec);
  }

  @Transactional()
  async all() {
    const spec = new QuerySpecification<UserEntity>().withStatement(
      this.usersCypher,
    );

    return this.persistenceManager.query(spec);
  }

  @Transactional()
  async findById(id: string) {
    const spec = new QuerySpecification<UserEntity>()
      .withStatement(this.usersCypher)
      .bind({ id });

    return this.persistenceManager.getOne(spec);
  }
}

Anyone facing the same issue ?

I think it may be related to nest version, as I'm using the following one:

Extract of package.json

    "@nestjs/common": "^8.0.0",
    "@nestjs/config": "^1.0.1",
    "@nestjs/core": "^8.0.0",
    "@nestjs/graphql": "^8.0.2",
    "@nestjs/passport": "^8.0.1",
    "@nestjs/platform-express": "^8.0.0",

I don't really want to downgrade my nest version to get it working.

magrinj commented 3 years ago

Ok if anyone get there, I've just found the issue.

It's not really well described in the quick-start, but as soon as you are providing a name to DatabaseRegistry.buildOrResolveFromEnv('NEO4J'). You need to reflect this one in the InjectPersistenceManager like that: @InjectPersistenceManager('NEO4J')

jasperblues commented 3 years ago

@magrinj The intention of this feature is to support multiple databases. There can be one nameless/default one, others will be named.

How do you think we can make this clearer in the quick start and docs? What would you have liked to see?

magrinj commented 3 years ago

@jasperblues Thanks for the help, I've understand that this project is database idiomatic.

My problems was the following:

1 - In the quick-start here: https://drivine.org/guide/#/quick-start

In the first section when you declare the main driving module, a database concatenation name is provided (NEO):

@Module({
    imports: [
        DrivineModule.withOptions(<DrivineModuleOptions>{
            connectionProviders: [DatabaseRegistry.buildOrResolveFromEnv('NEO')]
        })
    ],
    providers: [RouteRepository],
    controllers: [RouteController]
})
export class AppModule implements NestModule {}

But few lines under, when the repository is created no database name is provided:

@Injectable()
export class RouteRepository {
    constructor(
        @InjectPersistenceManager() readonly persistenceManager: PersistenceManager,
        @InjectCypher('@/traffic/routesBetween') readonly routesBetween: Statement
    ) {}

    @Transactional() // Has default Propagation.REQUIRED - so partipicate in a current txn, or start one.
    async findFastestBetween(start: string, destination: string): Promise<Route> {
        return this.persistenceManager.getOne(
            new QuerySpecification<Route>()
                .withStatement(this.routesBetween)
                .bind([start, destination])
                .limit(1)
                .transform(Route)
        );
    }
}

So, by just following the quick start you will get an error related to PersistenceManager not resolved.

2 - As mentioned in #47, some classes was imported from wrong places

If you go in the sample-project, the classes are also imported from the path where it's not working:

// NOT WORKING
import { DrivineModule } from '@liberation-data/drivine/DrivineModule';
import { DatabaseRegistry } from '@liberation-data/drivine/connection/DatabaseRegistry';

// WORKING
import { DrivineModule, DatabaseRegistry } from '@liberation-data/drivine';

In the sample: https://github.com/liberation-data/drivine-inspiration/blob/9df519e3c26faf65b56d274f81c8d49d0401235f/src/AppModule.ts#L5

3 - Cypher files not copied in dist folder

I've fixed that other problem by updating nest-cli.json:

{
  "collection": "@nestjs/schematics",
  "sourceRoot": "src",
  "compilerOptions": {
    "assets": [
      { "include": "**/*.cypher", "outDir": "dist/src", "watchAssets": true }
    ]
  }
}

It was hard to integrate this lib for me with NestJS from scratch, I got the above issues that can be avoided by upgrading a bit the documentation.

jasperblues commented 3 years ago

Thanks for the clarification @magrinj

Regarding issue #47 I don't think there was a clear specific issue, but it seems to have converged on import style.

I will update the docs in the quickstart and elsewhere. If you come across another place where the are incorrect, it would be great if you could let me know.

We should also have a place to let people know how to set up nest-cli.

jasperblues commented 3 years ago

@magrinj Actually I do not see a place in the quick start where imports are shown. Do you mean in the starter app? Or could you point out where?

magrinj commented 3 years ago

@jasperblues For the #47 issue it was related for me to the import style as explained above.

This import is shown on the sample-projet. I've put a direct link on the line in my comment above.

For the quick-start the problem was not the import but the fact that a database named NEO is provided on initialization, but this name is not reflected in the InjectPersistenceManager.

Hope it's more clear, if it's not let me know

jasperblues commented 3 years ago

@magrinj Sorry for the slow reply! OK, its clear where the struggle was. Do you think the following would help:

Do any of the above would provide the path of least confusion? I want folks to have a fun time setting up.

magrinj commented 3 years ago

@jasperblues I think that both of the above should help and prevent struggling during setup :)

The imports should be fixed too in the sample and shown in the quick start to avoid confusion.

For the cypher files not copied with Nest.JS a troubleshooting section can be added, or a Nest.JS section with the code pasted above.

dominiklippl commented 3 years ago

This should really be in the documentation, I spent several hours fixing this bug until I found this issue. Many many many thanks to @magrinj finally I can continue working.

jasperblues commented 3 years ago

@dominiklippl, Jérémy M described two issues. Which one did you encounter?

dominiklippl commented 3 years ago

The first (PersistenceManager:default not resolved) and third Issue (Cypher files not copied in dist folder)

jasperblues commented 3 years ago

@dominiklippl If you like, I can invite you as a contributor - this will give wiki access, and you may edit as you see fit, to make it clearer, so that others don't get stuck on the same steps.

This is a non-profit project - I try to work on it as much as I can and hopefully it turns out to be useful for others. I'm always happy to have contributions from the community.

The same goes for the sample app, if anyone would like to send a PR to tidy the imports there.

dominiklippl commented 3 years ago

@jasperblues That would be great

jasperblues commented 3 years ago

@dominiklippl Done. You now have write access.

THANK YOU for helping out!