loop-payments / prisma-lint

A linter for Prisma schema files.
https://www.npmjs.com/package/prisma-lint
MIT License
120 stars 4 forks source link

field-name-mapping-snake-case rule have an issue with MongoDB ID field. #330

Closed daeteck closed 3 months ago

daeteck commented 4 months ago

Prisma documentation says that an id in MongoDb should be defined like this:

model Post { id String @id @default(uuid()) @map("_id") text String }

Where the id name of the primary key is mapped to MongoDB _id convention.

The issue is that if the rule field-name-mapping-snake-case it's enabled prisma-lint it throws this error:

Post.id x:x error Field name must be mapped to "id". field-name-mapping-snake-case

maxh commented 4 months ago

Interesting. Thanks for reporting. We could add an option requirePrefixUnderscore to that rule's configuration.

maxh commented 4 months ago

I see your discussion here:

https://github.com/iiian/prisma-case-format/issues/59

Currently prisma-lint has no vendor-specific business logic; everything is just based on the vendor-agnostic Prisma schema AST. I can see advantages either way, but at the moment I'm inclined to do the simpler option of making this configurable. There may be other use cases for prefixes. Perhaps a requirePrefix configuration option could be set to _ for this use case.

maxh commented 4 months ago

@daeteck try this config for field-name-mapping-snake-case in 0.1.2:

{
  "requirePrefix": "_",
}
daeteck commented 3 months ago

Hi @maxh,

Unfortunately if I put the requirePrefix all the fields that I have mapped are throwing that the prefix is required.

This is my schema, and an example of an issue that the lint should warning is for example in the model Location the field timeZone that is not mapped to time_zone.

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "mongodb"
  url      = env("DATABASE_COMMON_URL")
}

model Location {
  id                           String                            @id @default(uuid()) @map("_id")
  name                         String
  code                         String
  timeZone                     String
  locale                       String /// See https://www.rfc-editor.org/rfc/rfc5646.html
  additionalInformation        LocationAdditionalInformation?
  additionalInformationWeb     LocationAdditionalInformationWeb?
  gameLocation                 GameLocation[]
  transactionChannelPermission TransactionChannelPermission[]
  seed                         Seed[]
  pointOfSale                  PointOfSale[]
  channelPaymentMethod         ChannelPaymentMethod[]
  locationCurrencyEnabled      LocationCurrencyEnabled[]
  enabled                      Boolean                           @default(true)
  createdAt                    DateTime                          @default(now()) @map("created_at")
  updatedAt                    DateTime                          @updatedAt @map("updated_at")

  @@unique([name], map: "location_name_idx")
  @@unique([code], map: "location_code_idx")
  @@unique([name, code], map: "location_name_code_idx")
  @@unique([id, enabled], map: "location_id_enabled_idx")
  @@map("location")
}

model Currency {
  id                      String                    @id @default(uuid()) @map("_id")
  code                    String /// ISO 4217 code (https://en.wikipedia.org/wiki/ISO_4217) o cryptocurrencies symbol  (https://en.wikipedia.org/wiki/List_of_cryptocurrencies). Para currency "humo", inventar codigo
  name                    String /// ISO 4217 name (https://en.wikipedia.org/wiki/ISO_4217) o cryptocurrencies currency  (https://en.wikipedia.org/wiki/List_of_cryptocurrencies). Para currency "humo", inventar nombre
  symbol                  String /// Simbolo en utf https://en.wikipedia.org/wiki/Currency_symbol
  locationCurrencyEnabled LocationCurrencyEnabled[]
  decimalPlaces           Int                       @map("decimal_places") /// cantidad de decimales que utiliza
  createdAt               DateTime                  @default(now()) @map("created_at")
  updatedAt               DateTime                  @updatedAt @map("updated_at")

  @@unique([code], map: "currency_code_idx")
  @@map("currency")
}

model LocationCurrencyEnabled {
  id         String   @id @default(uuid()) @map("_id")
  location   Location @relation(fields: [locationId], references: [id])
  locationId String   @map("location_id")
  currency   Currency @relation(fields: [currencyId], references: [id])
  currencyId String   @map("currency_id")

  @@unique([locationId, currencyId], map: "location_currency_location_id_currency_id_idx")
  @@index([locationId], map: "location_currency_enabled_location_id_idx")
  @@index([currencyId], map: "location_currency_enabled_currency_id_idx")
  @@map("location_currency_enabled")
}

model Game {
  id                           String                         @id @default(uuid()) @map("_id")
  name                         String
  code                         String /// Example: QN Quiniela Nocturna
  gameFamily                   GameFamily                     @relation(fields: [gameFamilyId], references: [id])
  gameFamilyId                 String                         @map("game_family_id")
  gameLocation                 GameLocation[]
  transactionChannelPermission TransactionChannelPermission[]
  createdAt                    DateTime                       @default(now()) @map("created_at")
  updatedAt                    DateTime                       @updatedAt @map("updated_at")

  @@unique([name], map: "game_name_idx")
  @@unique([code], map: "game_code_idx")
  @@index([gameFamilyId], map: "game_game_family_id_idx")
  @@map("game")
}

model GameFamily {
  id        String   @id @default(uuid()) @map("_id")
  name      String
  game      Game[]
  createdAt DateTime @default(now()) @map("created_at")
  updatedAt DateTime @updatedAt @map("updated_at")

  @@unique([name], map: "game_family_name_idx")
  @@map("game_family")
}

model GameLocation {
  id           String   @id @default(uuid()) @map("_id")
  game         Game     @relation(fields: [gameId], references: [id])
  gameId       String   @map("game_id")
  location     Location @relation(fields: [locationId], references: [id])
  locationId   String   @map("location_id")
  enabled      Boolean  @default(false) /// To disable the game by location
  localization String /// For example in Entre Rios Quiniela is Tombola.
  sequenceFrom Int      @map("sequence_from") /// Number where the numeration of the ticket starts by draw.
  createdAt    DateTime @default(now()) @map("created_at")
  updatedAt    DateTime @updatedAt @map("updated_at")

  @@unique([gameId, locationId], map: "game_location_game_id_location_id_idx")
  @@index([gameId], map: "game_location_game_id_idx")
  @@index([locationId], map: "game_location_location_id_idx")
  @@map("game_location")
}

/// Example: Venta, Extracto, Cancelacion, Reporte de Caja, Memo, Reporte de premios, etc.
model TransactionType {
  id                           String                         @id @default(uuid()) @map("_id")
  name                         String
  transactionChannelPermission TransactionChannelPermission[]
  createdAt                    DateTime                       @default(now()) @map("created_at")
  updatedAt                    DateTime                       @updatedAt @map("updated_at")

  @@unique([name], map: "transaction_type_name_idx")
  @@map("transaction_type")
}

model TransactionChannelPermission {
  id                String          @id @default(uuid()) @map("_id")
  saleChannel       SaleChannel     @map("sale_channel")
  game              Game?           @relation(fields: [gameId], references: [id])
  gameId            String?         @map("game_id")
  location          Location        @relation(fields: [locationId], references: [id])
  locationId        String          @map("location_id")
  transactionType   TransactionType @relation(fields: [transactionTypeId], references: [id])
  transactionTypeId String          @map("transaction_type_id")
  enabled           Boolean         @default(false)
  createdAt         DateTime        @default(now()) @map("created_at")
  updatedAt         DateTime        @updatedAt @map("updated_at")

  @@unique([saleChannel, gameId, locationId, transactionTypeId], map: "transaction_sale_channel_permission_channel_game_id_location_id_transaction_type_id_idx")
  @@index([gameId], map: "transaction_channel_permission_game_id_idx")
  @@index([locationId], map: "transaction_channel_permission_location_id_idx")
  @@index([transactionTypeId], map: "transaction_channel_permission_channel_transaction_type_id_idx")
  @@map("transaction_channel_permission")
}

model PaymentMethod {
  id                   String                 @id @default(uuid()) @map("_id")
  name                 String
  channelPaymentMethod ChannelPaymentMethod[]
  createdAt            DateTime               @default(now()) @map("created_at")
  updatedAt            DateTime               @updatedAt @map("updated_at")

  @@unique([name], map: "payment_method_name_idx")
  @@map("payment_method")
}

model UserIdentificationType {
  id                 String   @id @default(uuid()) @map("_id")
  name               String
  code               String // Example: DNI, RUT, PASS
  regexp             String?
  validationFunction String?  @map("validation_function")
  createdAt          DateTime @default(now()) @map("created_at")
  updatedAt          DateTime @updatedAt @map("updated_at")

  @@unique([name], map: "user_identification_type_name_idx")
  @@unique([code], map: "user_identification_type_code_idx")
  @@map("user_identification_type")
}

model ChannelPaymentMethod {
  id              String        @id @default(uuid()) @map("_id")
  saleChannel     SaleChannel   @map("sale_channel")
  paymentMethod   PaymentMethod @relation(fields: [paymentMethodId], references: [id])
  paymentMethodId String        @map("payment_method_id")
  location        Location      @relation(fields: [locationId], references: [id])
  locationId      String        @map("location_id")
  createdAt       DateTime      @default(now()) @map("created_at")
  updatedAt       DateTime      @updatedAt @map("updated_at")

  @@unique([saleChannel, paymentMethodId, locationId], map: "sale_channel_payment_method_channel_payment_method_id_location_id_idx")
  @@index([paymentMethodId], map: "channel_payment_payment_method_id_idx")
  @@index([locationId], map: "channel_payment_location_id_idx")
  @@map("channel_payment_method")
}

model Seed {
  id         String   @id @default(uuid()) @map("_id")
  hash       String
  validFrom  DateTime @map("valid_from") /// Saved as UTC
  validTo    DateTime @map("valid_to") /// Saved as UTC
  location   Location @relation(fields: [locationId], references: [id])
  locationId String   @map("location_id")
  createdAt  DateTime @default(now()) @map("created_at")
  updatedAt  DateTime @updatedAt @map("updated_at")

  @@unique([hash, locationId], map: "seed_hash_location_id_idx")
  @@index([validFrom, validTo, locationId], map: "seed_valid_from_valid_to_location_id_idx") /// User to filter by the currect valid seed to use.
  @@index([locationId], map: "seed_location_id_idx")
  @@map("seed")
}

model PointOfSale {
  id           String      @id @default(uuid()) @map("_id")
  externalCode String      @map("external_code") ///Ex: 007002000000
  saleChannel  SaleChannel @map("sale_channel")
  location     Location    @relation(fields: [locationId], references: [id])
  locationId   String      @map("location_id")
  createdAt    DateTime    @default(now()) @map("created_at")
  updatedAt    DateTime    @updatedAt @map("updated_at")

  @@index([locationId], map: "point_of_sale_location_id_idx")
  @@map("point_of_sale")
}

type LocationAdditionalInformation {
  institution       String? /// Ticket title in most of the cases
  printExtraBarCode Boolean? @map("print_extra_bar_code") /// Si se imprime el código extra de barras por ejemplo en Córdoba
  taxMessage        String?  @map("tax_message")
}

type LocationAdditionalInformationWeb {
  geolocation Boolean @default(false) /// Whether the web channel should track a customer's geolocation or not.
  sessionTime Int     @default(300) @map("session_time") /// Active time of session
}

enum SaleChannel {
  TERMINAL
  WEB
  AUTOSERVICE
  MOBILE
  TELEINFOR
  APP_AGENCIERO
}

libs/database-library/common/prisma/schema.prisma ✖ ChannelPaymentMethod.createdAt 198:3 error Field name must be mapped to "_created_at". field-name-mapping-snake-case ChannelPaymentMethod.locationId 197:3 error Field name must be mapped to "_location_id". field-name-mapping-snake-case ChannelPaymentMethod.paymentMethodId 195:3 error Field name must be mapped to "_payment_method_id". field-name-mapping-snake-case ChannelPaymentMethod.updatedAt 199:3 error Field name must be mapped to "_updated_at". field-name-mapping-snake-case Currency.createdAt 68:3 error Field name must be mapped to "_created_at". field-name-mapping-snake-case Currency.decimalPlaces 67:3 error Field name must be mapped to "_decimal_places". field-name-mapping-snake-case Currency.updatedAt 69:3 error Field name must be mapped to "_updated_at". field-name-mapping-snake-case Game.createdAt 96:3 error Field name must be mapped to "_created_at". field-name-mapping-snake-case Game.gameFamilyId 93:3 error Field name must be mapped to "_game_family_id". field-name-mapping-snake-case Game.updatedAt 97:3 error Field name must be mapped to "_updated_at". field-name-mapping-snake-case GameFamily.createdAt 109:3 error Field name must be mapped to "_created_at". field-name-mapping-snake-case GameFamily.updatedAt 110:3 error Field name must be mapped to "_updated_at". field-name-mapping-snake-case GameLocation.createdAt 125:3 error Field name must be mapped to "_created_at". field-name-mapping-snake-case GameLocation.gameId 119:3 error Field name must be mapped to "_game_id". field-name-mapping-snake-case GameLocation.locationId 121:3 error Field name must be mapped to "_location_id". field-name-mapping-snake-case GameLocation.sequenceFrom 124:3 error Field name must be mapped to "_sequence_from". field-name-mapping-snake-case GameLocation.updatedAt 126:3 error Field name must be mapped to "_updated_at". field-name-mapping-snake-case Location.createdAt 51:3 error Field name must be mapped to "_created_at". field-name-mapping-snake-case Location.timeZone 40:3 error Field name must be mapped to snake case. field-name-mapping-snake-case Location.updatedAt 52:3 error Field name must be mapped to "_updated_at". field-name-mapping-snake-case LocationCurrencyEnabled.currencyId 80:3 error Field name must be mapped to "_currency_id". field-name-mapping-snake-case LocationCurrencyEnabled.locationId 78:3 error Field name must be mapped to "_location_id". field-name-mapping-snake-case PaymentMethod.createdAt 170:3 error Field name must be mapped to "_created_at". field-name-mapping-snake-case PaymentMethod.updatedAt 171:3 error Field name must be mapped to "_updated_at". field-name-mapping-snake-case PointOfSale.createdAt 229:3 error Field name must be mapped to "_created_at". field-name-mapping-snake-case PointOfSale.externalCode 225:3 error Field name must be mapped to "_external_code". field-name-mapping-snake-case PointOfSale.locationId 228:3 error Field name must be mapped to "_location_id". field-name-mapping-snake-case PointOfSale.updatedAt 230:3 error Field name must be mapped to "_updated_at". field-name-mapping-snake-case Seed.createdAt 214:3 error Field name must be mapped to "_created_at". field-name-mapping-snake-case Seed.locationId 213:3 error Field name must be mapped to "_location_id". field-name-mapping-snake-case Seed.updatedAt 215:3 error Field name must be mapped to "_updated_at". field-name-mapping-snake-case Seed.validFrom 210:3 error Field name must be mapped to "_valid_from". field-name-mapping-snake-case Seed.validTo 211:3 error Field name must be mapped to "_valid_to". field-name-mapping-snake-case TransactionChannelPermission.createdAt 156:3 error Field name must be mapped to "_created_at". field-name-mapping-snake-case TransactionChannelPermission.gameId 150:3 error Field name must be mapped to "_game_id". field-name-mapping-snake-case TransactionChannelPermission.locationId 152:3 error Field name must be mapped to "_location_id". field-name-mapping-snake-case TransactionChannelPermission.transactionTypeId 154:3 error Field name must be mapped to "_transaction_type_id". field-name-mapping-snake-case TransactionChannelPermission.updatedAt 157:3 error Field name must be mapped to "_updated_at". field-name-mapping-snake-case TransactionType.createdAt 139:3 error Field name must be mapped to "_created_at". field-name-mapping-snake-case TransactionType.updatedAt 140:3 error Field name must be mapped to "_updated_at". field-name-mapping-snake-case UserIdentificationType.createdAt 183:3 error Field name must be mapped to "_created_at". field-name-mapping-snake-case UserIdentificationType.updatedAt 184:3 error Field name must be mapped to "_updated_at". field-name-mapping-snake-case UserIdentificationType.validationFunction 182:3 error Field name must be mapped to "_validation_function". field-name-mapping-snake-case

maxh commented 3 months ago

Apologies, I think this is what we need here, please confirm: https://github.com/loop-payments/prisma-lint/pull/362

maxh commented 3 months ago

@daeteck please try this config in 0.3.0:

{
  "requireUnderscorePrefixForIds": true,
}
daeteck commented 3 months ago

Thank you @maxh

It works perfect!