SoftwareBrothers / adminjs

AdminJS is an admin panel for apps written in node.js
https://adminjs.co
MIT License
8.07k stars 650 forks source link

[Bug]: AdminJs/Relations One to Many TypeError: Cannot read properties of null (reading 'type') #1619

Closed ZanderFoster closed 4 months ago

ZanderFoster commented 5 months ago

Contact Details

No response

What happened?

When trying to follow the One to Many example I am getting an error that looks awfully similar to https://github.com/SoftwareBrothers/adminjs/issues/1616#issue-2113475705

I've tried the suggestions in https://github.com/SoftwareBrothers/adminjs/issues/1616#issue-2113475705 and https://github.com/SoftwareBrothers/adminjs/issues/1595#issue-2050838019

Nothing seems to relieve the error,

When switching to "app" instead of "appId" I get the following error

Invalid prisma.licenseKey.findMany() invocation:

{
  where: {
    id: 1
  },
  skip: 0,
  take: 10,
  orderBy: {
    name: "asc",
    ~~~~
?   id?: SortOrder,
?   createdAt?: SortOrder,
?   updatedAt?: SortOrder,
?   lastUsed?: SortOrder | SortOrderInput,
?   Duration?: SortOrder,
?   appId?: SortOrder,
?   userId?: SortOrder | SortOrderInput,
?   key?: SortOrder,
?   app?: ApplicationOrderByWithRelationInput,
?   user?: UserOrderByWithRelationInput
}

Unknown argument name. Did you mean app? Available options are marked with ?.

Bug prevalence

Unable to get code working at all.

AdminJS dependencies version

    "@adminjs/express": "^6.1.0",
    "@adminjs/logger": "^5.0.1",
    "@adminjs/passwords": "^4.0.0",
    "@adminjs/prisma": "^5.0.2",
    "@adminjs/relations": "^1.0.1",
    "@adminjs/themes": "^1.0.1",
    "@emotion/react": "^11.11.3",
    "@emotion/styled": "^11.11.0",
    "@mui/icons-material": "^5.15.7",
    "@mui/material": "^5.15.7",
    "@prisma/client": "^5.6.0",
    "adminjs": "^7.4.1",
    "argon2": "^0.31.2",
    "express": "^4.18.2",
    "express-formidable": "^1.2.0",
    "express-session": "^1.17.3",
    "pg": "latest"
  },```

### What browsers do you see the problem on?

_No response_

### Relevant log output

```bash
TypeError: Cannot read properties of null (reading 'type')

Relevant code that's giving you issues

model Application {
  id          Int      @id @default(autoincrement())
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt
  name        String   @unique
  secret      String   @default(uuid())
  version     String
  licenses    LicenseKey[]

  @@map("applications")
}

model LicenseKey {
  id          Int           @id @default(autoincrement())
  createdAt   DateTime      @default(now())
  updatedAt   DateTime      @updatedAt
  lastUsed    DateTime?
  Duration    Int      
  appId       Int           @map("app_id") 
  app         Application   @relation(fields: [appId], references: [id])
  userId      Int?          @map("user_id")
  user        User?         @relation(fields: [userId], references: [id])
  key         String        @default(uuid())

  @@map("license_keys")
}

owningRelationSettingsFeature({
            componentLoader,
            licenseKey: process.env.ADMINJS_RELATIONS_LICENSEKEY,
            relations: {
                keys: {
                    type: RelationType.OneToMany,
                    target: {
                        joinKey: 'appId',
                        resourceId: 'LicenseKey',
                    },
                },
            },
        }),
dziraf commented 5 months ago

@ZanderFoster I've just tested in our test app with Organization and Person:

model Organization {
    id      Int      @id @default(autoincrement())
    name    String
    persons Person[]

    @@map("organizations")
}

model Person {
    id                  Int          @id @default(autoincrement())
    firstName           String       @map("first_name")
    lastName            String       @map("last_name")
    email               String
    organization        Organization @relation(fields: [organizationId], references: [id])
    organizationId      Int          @map("organization_id")

    teams               TeamMember[]

    @@map("persons")
}

Resource:

      {
        resource: { model: getModelByName('Organization'), client: prisma },
        features: [
          owningRelationSettingsFeature({
            componentLoader,
            licenseKey: process.env.LICENSE_KEY,
            relations: {
              persons: {
                type: RelationType.OneToMany,
                target: {
                  joinKey: 'organization',
                  resourceId: 'Person',
                },
              },
            },
          }),
        ],
      }

This works for me which is similar to your setup. Do you have any extra resource options in LicenseKey or Application resources?

ZanderFoster commented 5 months ago

@dziraf

Nothing that I can tell would interfere, maybe you can spot something?

Application

import { getModelByName } from '@adminjs/prisma'
import { PrismaClient } from '@prisma/client';

import loggerFeature from '@adminjs/logger';
import componentLoader from '../admin/component-loader.js';
import { isAdmin } from '../admin/roles.js';

const hasAccess = ({ currentAdmin }) => currentAdmin && isAdmin(currentAdmin.email);

const prisma = new PrismaClient();

const panelNavigation =
{
    name: 'Panel',
    icon: 'Shield',
}

export const createApplicationResource = () => (
{
    resource: {
        model: getModelByName('Application'),
        client: prisma,
    },
    options: {
        navigation: panelNavigation,
        sort: {
            sortBy: 'name',
            direction: 'desc',
        },
        actions: {
            new: {
                isAccessible: hasAccess,
            },
            show: {
                isAccessible: hasAccess,
            },
            edit: {
                isAccessible: hasAccess,
            },
            list: {
                isAccessible: hasAccess,
            },
        },
        properties: {
            id: {
                isVisible: false,
            },
            createdAt: {
                isVisible: false,
            },
            updatedAt: {
                isVisible: false,
            },
            secret: {
                isVisible: {
                    list: false,
                    show: true,
                    edit: false,
                    filter: false,
                },
            },
        },
    },
    features: [
        loggerFeature({
            componentLoader,
            propertiesMapping: {
                user: 'userId',
            },
            userIdAttribute: 'email',
        }),
        owningRelationSettingsFeature({
            componentLoader,
            licenseKey: process.env.ADMINJS_RELATIONS_LICENSEKEY,
            relations: {
                keys: {
                    type: RelationType.OneToMany,
                    target: {
                        joinKey: 'appId',
                        resourceId: 'LicenseKey',
                    },
                },
            },
        }),
    ]
});

License:


import { getModelByName } from '@adminjs/prisma'
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

export const createLicenseKeyResource = () => ({
  resource: {
    model: getModelByName('LicenseKey'),
    client: prisma,
  },
  options: {
    properties: { 
        id: {
            isVisible: false,
        },
        lastUsed: {
            isVisible: false,
        },
        updatedAt: {
            isVisible: false,
        },
        key: {
            isVisible: {
                list: false,
                show: true,
                edit: false,
                filter: false,
            },
        },
    },
  },
  features: [targetRelationSettingsFeature()],
});
ZanderFoster commented 5 months ago

@dziraf I've removed the options section from both applications and licenses, still getting the null (reading 'type') error.

dziraf commented 5 months ago

It should work if you change appId to app - @adminjs/prisma uses relation names from schema to query the database.

I think the sorting error you're getting is due to:

        sort: {
            sortBy: 'name',
            direction: 'desc',
        },

It should probably work if you remove. I'll take a look at the code why it causes the error though.

ZanderFoster commented 5 months ago

It should work if you change appId to app - @adminjs/prisma uses relation names from schema to query the database.

I think the sorting error you're getting is due to:

        sort: {
            sortBy: 'name',
            direction: 'desc',
        },

It should probably work if you remove. I'll take a look at the code why it causes the error though.

That did the trick, Seems to be loading correctly now.