Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue with adapter for adminjs/mongoose #1697

Open
bogdan-kocic opened this issue Aug 7, 2024 · 6 comments
Open

Issue with adapter for adminjs/mongoose #1697

bogdan-kocic opened this issue Aug 7, 2024 · 6 comments
Labels
bug Something isn't working

Comments

@bogdan-kocic
Copy link

bogdan-kocic commented Aug 7, 2024

Contact Details

No response

What happened?

After starting application I receive this error: There are no adapters supporting one of the resource you provided.
I'm using @adminjs/mongoose adapter.

Bug prevalence

always

AdminJS dependencies version

"dependencies": {
"@adminjs/express": "^6.1.0",
"@adminjs/mongoose": "^4.1.0",
"@adminjs/nestjs": "^6.1.0",
"@nestjs/common": "^10.0.0",
"@nestjs/config": "^3.2.2",
"@nestjs/core": "^10.0.0",
"@nestjs/jwt": "^10.2.0",
"@nestjs/mongoose": "^10.0.6",
"@nestjs/passport": "^10.0.3",
"@nestjs/platform-express": "^10.0.0",
"@nestjs/throttler": "^5.2.0",
"@nestjs/typeorm": "^10.0.2",
"@types/nodemailer": "^6.4.15",
"@vercel/node": "^3.1.7",
"bcrypt": "^5.1.1",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"compression": "^1.7.4",
"express-formidable": "^1.2.0",
"express-session": "^1.18.0",
"firebase-admin": "^12.2.0",
"handlebars": "^4.7.8",
"helmet": "^7.1.0",
"mongodb": "^5.9.2",
"mongoose": "^8.4.3",
"nanoid": "^3.3.7",
"nestjs-seeder": "^0.3.2",
"nodemailer": "^6.9.14",
"passport": "^0.7.0",
"passport-jwt": "^4.0.1",
"passport-local": "^1.0.0",
"reflect-metadata": "^0.2.0",
"rxjs": "^7.8.1",
"slugify": "^1.6.6",
"typeorm": "^0.3.20",
"zod": "^3.23.8"
},
"devDependencies": {
"@nestjs/cli": "^10.0.0",
"@nestjs/schematics": "^10.0.0",
"@nestjs/testing": "^10.3.10",
"@types/bcrypt": "^5.0.2",
"@types/express": "^4.17.17",
"@types/jest": "^29.5.2",
"@types/node": "^20.3.1",
"@types/passport-jwt": "^4.0.1",
"@types/passport-local": "^1.0.38",
"@types/supertest": "^6.0.0",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
"eslint": "^8.42.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
"jest": "^29.5.0",
"prettier": "^3.0.0",
"source-map-support": "^0.5.21",
"supertest": "^6.3.3",
"ts-jest": "^29.1.0",
"ts-loader": "^9.4.3",
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.1.3"
}

What browsers do you see the problem on?

Chrome

Relevant log output

NoResourceAdapterError: There are no adapters supporting one of the resource you provided
    at file:///*/*/*/*/*/node_modules/adminjs/lib/backend/utils/resources-factory/resources-factory.js:73:15

Relevant code that's giving you issues

Mongo schema:

import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import * as mongoose from 'mongoose';
import { Entity } from './entity.schema';
import { Document } from 'mongoose';
import { softDeletePlugin } from './soft_delete/soft-delete-plugin';

@Schema()
export class Appointment extends Document {
  @Prop({ type: mongoose.Schema.Types.ObjectId, ref: Entity.name })
  entity: Entity;
  @Prop({ type: Object, required: true })
  conversation_log: object;
}

export type AppointmentDocument = Appointment & Document;
const AppointmentSchema = SchemaFactory.createForClass(Appointment);

AppointmentSchema.plugin(softDeletePlugin);

export { AppointmentSchema };

App module:

import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { ConfigModule } from '@nestjs/config';
import * as process from 'process';
import { AppointmentsModule } from './appointments/appointments.module';
import { UsersModule } from './users/user.module';
import { AuthModule } from './auth/auth.module';
import { FilterModule } from './filters/filter.module';
import { EntitiesModule } from './entities/entities.module';
import { CitiesModule } from './cities/cities.module';
import { MailService } from './services/mail.service';
import { Appointment } from './schemas/appointment.schema';

export const dynamicImport = async (packageName: string) =>
  new Function(`return import('${packageName}')`)();

const DEFAULT_ADMIN = {
  email: '',
  password: '',
};

const authenticate = async (email: string, password: string) => {
  if (email === DEFAULT_ADMIN.email && password === DEFAULT_ADMIN.password) {
    return Promise.resolve(DEFAULT_ADMIN);
  }
  return null;
};

@Module({
  controllers: [],
  imports: [
    ConfigModule.forRoot({ isGlobal: true }),
    MongooseModule.forRootAsync({
      useFactory: async () => ({
        uri: process.env.DB_CONNECTION_URI,
      }),
    }),
    dynamicImport('@adminjs/nestjs').then(({ AdminModule }) =>
      AdminModule.createAdminAsync({
        useFactory: () => ({
          adminJsOptions: {
            rootPath: '/admin',
            resources: [Appointment],
          },
          auth: {
            authenticate,
            cookieName: '',
            cookiePassword: '',
          },
          sessionOptions: {
            resave: true,
            saveUninitialized: true,
            secret: 'secret',
          },
        }),
      }),
    ),
    AppointmentsModule,
    UsersModule,
    AuthModule,
    FilterModule,
    EntitiesModule,
    CitiesModule,
  ],
  providers: [MailService],
})
export class AppModule {}
@bogdan-kocic bogdan-kocic added the bug Something isn't working label Aug 7, 2024
@dziraf
Copy link
Contributor

dziraf commented Aug 8, 2024

I haven't used @nestjs/mongoose personally but if it returns different metadata than mongoose, it won't work and would require another adapter just for it.

However, you might be missing AdminJS.registerAdapter({ Database, Resource }) because I cannot find it in the code you shared.

@bogdan-kocic
Copy link
Author

Hey @dziraf, thank you for responding. I'm relatively new to nodejs. Can you please explain me what do you mean by mongoose. With what should I compare @nestjs/mongoose to?

Sorry, I didn't provide my main.ts, here is where I register the adapter.

import { NestFactory, Reflector } from '@nestjs/core';
import { AppModule, dynamicImport } from './app.module';
import helmet from 'helmet';
import { JwtAuthGuard } from './auth/guards/jwt.guard';
import * as admin from 'firebase-admin';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  const reflector = app.get(Reflector);
  app.useGlobalGuards(new JwtAuthGuard(reflector));

  app.enableCors({ origin: '*' });
  app.use(helmet());

  const adminJSModule = await dynamicImport('adminjs');
  const AdminJS = adminJSModule.default;

  const AdminJSMongoose = await dynamicImport('@adminjs/mongoose');

  AdminJS.registerAdapter({
    Resource: AdminJSMongoose.Resource,
    Database: AdminJSMongoose.Database,
  });

  await app.listen(3000);
}
bootstrap();

@dziraf
Copy link
Contributor

dziraf commented Aug 8, 2024

  AdminJS.registerAdapter({
    Resource: AdminJSMongoose.Resource,
    Database: AdminJSMongoose.Database,
  });

Try placing that before bootstrap function or before const app = await .... Adapters have to be registered before AdminJS instance is created which is done at that step.

Can you please explain me what do you mean by mongoose. With what should I compare @nestjs/mongoose to?

Every adapter (@adminjs/mongoose, @adminjs/typeorm, etc) uses your ODM's/ORM's schemas/entitites to build AdminJS resources. mongoose is a MongoDB library which @adminjs/mongoose is integrated with. @nestjs/mongoose comes with it's own decorators and schema definition, so if resulting metadata is different to what mongoose returns, @adminjs/mongoose won't work with @nestjs/mongoose.

Alternatively, try providing AppointmentSchema instead of Appointment to resources

@bogdan-kocic
Copy link
Author

bogdan-kocic commented Aug 8, 2024

Greate, with AppointmentSchema it managed to register the adapter.
I have different error now:
TypeError: Cannot read properties of undefined (reading 'paths') at Resource.properties (file:///Users/*/*/*/*/node_modules/@adminjs/mongoose/lib/resource.js:47:57) at decorateDatabaseProperties (file:///Users/*/*/*/*/node_modules/adminjs/lib/backend/decorators/resource/utils/decorate-properties.js:9:19)

I have noticed another issue on this repo with exactly the same error. Did you manage to reproduce the error after pulling the repo.
Reference to the issue I'm talking about: #1257

I have tracked issue, it is located in node_modules/@adminjs/mongoose/lib/resource.js
return Object.entries(this.MongooseModel.schema.paths).map(([, path], position) => (new Property(path, position)));

If I change to 'this.MongooseModel.paths' it works, but again other things don't work.
For some reason this.MongooseModel structur is not as adminjs/resource.js expects.
For example, missing this.MongooseModel.db used in resource.js as well.

@bogdan-kocic
Copy link
Author

Hello @dziraf,

I can't make it work, what ever I do.
I cloned example project and it doesn't work aswell.

Do you have any more suggestions?

@bogdan-kocic
Copy link
Author

Hey @dziraf,

I managed to make it work by exporting another class from my schema.ts

export class City extends Document
export type CityDocument = City & Document;
const CityModel = mongoose.model('City', CitySchema); //new
export { CitySchema, CityModel };

Now I have db connection problems, I assume that CityModel class doesn't have to do anything with db connection?

error:
Operation cities.find() buffering timed out after 10000ms

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants