diff --git a/packages/client/src/generation/utils/buildDMMF.ts b/packages/client/src/generation/utils/buildDMMF.ts index 5e3215d08749..2a7ad7d22643 100644 --- a/packages/client/src/generation/utils/buildDMMF.ts +++ b/packages/client/src/generation/utils/buildDMMF.ts @@ -17,8 +17,8 @@ export function buildDMMF(compressed: boolean, dmmf: DMMF.Document) { return buildCompressedDMMF(dmmfString) } - const { datamodel, mappings } = dmmf - const dmmfString = escapeJson(JSON.stringify({ datamodel, mappings })) + const { datamodel } = dmmf + const dmmfString = escapeJson(JSON.stringify({ datamodel })) return buildUncompressedDMMF(dmmfString) } diff --git a/packages/client/src/runtime/core/extensions/visitQueryResult.ts b/packages/client/src/runtime/core/extensions/visitQueryResult.ts index 6c83d8c9b50c..fc5072924e70 100644 --- a/packages/client/src/runtime/core/extensions/visitQueryResult.ts +++ b/packages/client/src/runtime/core/extensions/visitQueryResult.ts @@ -1,6 +1,6 @@ import { DMMF } from '@prisma/generator-helper' -import { BaseDMMFHelper } from '../../dmmf' +import { DMMFDatamodelHelper } from '../../dmmf' import { JsArgs, Selection } from '../types/JsApi' type ModelVisitor = (value: object, model: DMMF.Model, queryArgs: JsArgs) => object | undefined @@ -9,7 +9,7 @@ type VisitParams = { result: object args: JsArgs model: DMMF.Model - dmmf: BaseDMMFHelper + dmmf: DMMFDatamodelHelper visitor: ModelVisitor } @@ -50,7 +50,7 @@ type VisitNestedParams = { includeOrSelect: Selection result: object parentModel: DMMF.Model - dmmf: BaseDMMFHelper + dmmf: DMMFDatamodelHelper visitor: ModelVisitor } diff --git a/packages/client/src/runtime/core/model/applyModel.ts b/packages/client/src/runtime/core/model/applyModel.ts index 8fc45e33bd08..90fc39bc6189 100644 --- a/packages/client/src/runtime/core/model/applyModel.ts +++ b/packages/client/src/runtime/core/model/applyModel.ts @@ -69,7 +69,7 @@ function modelMetaLayer(dmmfModelName: string): CompositeProxyLayer { function modelActionsLayer(client: Client, dmmfModelName: string): CompositeProxyLayer { // we use the javascript model name for display purposes const jsModelName = dmmfToJSModelName(dmmfModelName) - const ownKeys = getOwnKeys(client, dmmfModelName) + const ownKeys = Object.keys(DMMF.ModelAction).concat('count') return { getKeys() { @@ -127,15 +127,6 @@ function modelActionsLayer(client: Client, dmmfModelName: string): CompositeProx } } -function getOwnKeys(client: Client, dmmfModelName: string) { - const actionKeys = Object.keys(client._baseDmmf.mappingsMap[dmmfModelName]).filter( - (key) => key !== 'model' && key !== 'plural', - ) - actionKeys.push('count') - - return actionKeys -} - function isValidAggregateName(action: string): action is (typeof aggregateProps)[number] { return (aggregateProps as readonly string[]).includes(action) } diff --git a/packages/client/src/runtime/core/protocol/graphql.ts b/packages/client/src/runtime/core/protocol/graphql.ts index e7b762a7a48f..f891c83dc11b 100644 --- a/packages/client/src/runtime/core/protocol/graphql.ts +++ b/packages/client/src/runtime/core/protocol/graphql.ts @@ -2,7 +2,8 @@ import { EngineBatchQuery, GraphQLQuery } from '@prisma/engine-core' import { DMMFHelper } from '../../dmmf' import { ErrorFormat } from '../../getPrismaClient' -import { Args, Document, makeDocument, unpack } from '../../query' +import { Args, Document, makeDocument, PrismaClientValidationError, unpack } from '../../query' +import { createErrorMessageWithContext } from '../../utils/createErrorMessageWithContext' import { Action } from '../types/JsApi' import { CreateMessageOptions, ProtocolEncoder, ProtocolMessage } from './common' @@ -48,6 +49,14 @@ export class GraphQLProtocolEncoder implements ProtocolEncoder { } rootField = mapping[action === 'count' ? 'aggregate' : action] + if (!rootField) { + const message = createErrorMessageWithContext({ + message: `Model \`${modelName}\` does not support \`${action}\` action.`, + originalMethod: clientMethod, + callsite: callsite, + }) + throw new PrismaClientValidationError(message) + } } if (operation !== 'query' && operation !== 'mutation') { diff --git a/packages/client/src/runtime/core/protocol/json/JsonProtocolEncoder.ts b/packages/client/src/runtime/core/protocol/json/JsonProtocolEncoder.ts index 4b6503dd894b..64c3ba2a7abc 100644 --- a/packages/client/src/runtime/core/protocol/json/JsonProtocolEncoder.ts +++ b/packages/client/src/runtime/core/protocol/json/JsonProtocolEncoder.ts @@ -1,6 +1,6 @@ import { EngineBatchQuery, JsonQuery, JsonQueryAction } from '@prisma/engine-core' -import { BaseDMMFHelper } from '../../../dmmf' +import { DMMFDatamodelHelper } from '../../../dmmf' import { ErrorFormat } from '../../../getPrismaClient' import { deepGet } from '../../../utils/deep-set' import { CreateMessageOptions, ProtocolEncoder, ProtocolMessage } from '../common' @@ -8,7 +8,7 @@ import { deserializeJsonResponse } from './deserialize' import { serializeJsonQuery } from './serialize' export class JsonProtocolEncoder implements ProtocolEncoder { - constructor(private baseDmmf: BaseDMMFHelper, private errorFormat: ErrorFormat) {} + constructor(private baseDmmf: DMMFDatamodelHelper, private errorFormat: ErrorFormat) {} createMessage(createOptions: CreateMessageOptions): JsonProtocolMessage { const query = serializeJsonQuery({ ...createOptions, baseDmmf: this.baseDmmf, errorFormat: this.errorFormat }) diff --git a/packages/client/src/runtime/core/protocol/json/serialize.ts b/packages/client/src/runtime/core/protocol/json/serialize.ts index 2692f9b06f8c..95b32a451c07 100644 --- a/packages/client/src/runtime/core/protocol/json/serialize.ts +++ b/packages/client/src/runtime/core/protocol/json/serialize.ts @@ -9,7 +9,7 @@ import { import { DMMF } from '@prisma/generator-helper' import { assertNever } from '@prisma/internals' -import { BaseDMMFHelper } from '../../../dmmf' +import { DMMFDatamodelHelper } from '../../../dmmf' import { ErrorFormat } from '../../../getPrismaClient' import { ObjectEnumValue, objectEnumValues } from '../../../object-enums' import { CallSite } from '../../../utils/CallSite' @@ -45,7 +45,7 @@ const jsActionToProtocolAction: Record = { } export type SerializeParams = { - baseDmmf: BaseDMMFHelper + baseDmmf: DMMFDatamodelHelper modelName?: string action: Action args?: JsArgs @@ -247,7 +247,7 @@ function isRawParameters(value: JsInputValue): value is RawParameters { } type ContextParams = { - baseDmmf: BaseDMMFHelper + baseDmmf: DMMFDatamodelHelper originalMethod: string rootArgs: JsArgs | undefined extensions: MergedExtensionsList diff --git a/packages/client/src/runtime/dmmf-types.ts b/packages/client/src/runtime/dmmf-types.ts index 576f24b2bff9..e1c4735a49e7 100644 --- a/packages/client/src/runtime/dmmf-types.ts +++ b/packages/client/src/runtime/dmmf-types.ts @@ -2,4 +2,4 @@ import { DMMF } from '@prisma/generator-helper' export { DMMF } -export type BaseDMMF = Pick +export type BaseDMMF = Pick diff --git a/packages/client/src/runtime/dmmf.ts b/packages/client/src/runtime/dmmf.ts index 6703e13d6588..4173303b2962 100644 --- a/packages/client/src/runtime/dmmf.ts +++ b/packages/client/src/runtime/dmmf.ts @@ -1,11 +1,10 @@ import type { DMMF } from '@prisma/generator-helper' -import { BaseDMMF } from './dmmf-types' import { applyMixins } from './utils/applyMixins' import type { Dictionary } from './utils/common' import { keyBy, ScalarTypeTable } from './utils/common' -class DMMFDatamodelHelper implements Pick { +export class DMMFDatamodelHelper implements Pick { datamodel: DMMF.Datamodel datamodelEnumMap: Dictionary modelMap: Dictionary @@ -238,20 +237,11 @@ class DMMFSchemaHelper implements Pick { } } -export interface BaseDMMFHelper extends DMMFDatamodelHelper, DMMFMappingsHelper {} -export class BaseDMMFHelper { - constructor(dmmf: BaseDMMF) { - return Object.assign(this, new DMMFDatamodelHelper(dmmf), new DMMFMappingsHelper(dmmf)) - } -} - -applyMixins(BaseDMMFHelper, [DMMFDatamodelHelper, DMMFMappingsHelper]) - -export interface DMMFHelper extends BaseDMMFHelper, DMMFSchemaHelper {} +export interface DMMFHelper extends DMMFDatamodelHelper, DMMFMappingsHelper, DMMFSchemaHelper {} export class DMMFHelper { constructor(dmmf: DMMF.Document) { - return Object.assign(this, new BaseDMMFHelper(dmmf), new DMMFSchemaHelper(dmmf)) + return Object.assign(this, new DMMFDatamodelHelper(dmmf), new DMMFMappingsHelper(dmmf), new DMMFSchemaHelper(dmmf)) } } -applyMixins(DMMFHelper, [BaseDMMFHelper, DMMFSchemaHelper]) +applyMixins(DMMFHelper, [DMMFDatamodelHelper, DMMFMappingsHelper, DMMFSchemaHelper]) diff --git a/packages/client/src/runtime/getPrismaClient.ts b/packages/client/src/runtime/getPrismaClient.ts index b7a9d6b15bd5..bba985cc6804 100644 --- a/packages/client/src/runtime/getPrismaClient.ts +++ b/packages/client/src/runtime/getPrismaClient.ts @@ -58,7 +58,7 @@ import { } from './core/request/PrismaPromise' import { UserArgs } from './core/request/UserArgs' import { getLockCountPromise } from './core/transaction/utils/createLockCountPromise' -import { BaseDMMFHelper, DMMFHelper } from './dmmf' +import { DMMFDatamodelHelper, DMMFHelper } from './dmmf' import type { DMMF } from './dmmf-types' import { getLogLevel } from './getLogLevel' import { mergeBy } from './mergeBy' @@ -297,7 +297,7 @@ export type Client = ReturnType extends new () => infer export function getPrismaClient(config: GetPrismaClientConfig) { class PrismaClient { - _baseDmmf: BaseDMMFHelper + _baseDmmf: DMMFDatamodelHelper _dmmf?: DMMFHelper _engine: Engine _fetcher: RequestHandler @@ -395,7 +395,7 @@ export function getPrismaClient(config: GetPrismaClientConfig) { this._errorFormat = 'colorless' // default errorFormat } - this._baseDmmf = new BaseDMMFHelper(config.document) + this._baseDmmf = new DMMFDatamodelHelper(config.document) const engineProtocol = NODE_CLIENT ? getQueryEngineProtocol(config.generator) : config.edgeClientProtocol ?? getQueryEngineProtocol(config.generator) diff --git a/packages/client/src/runtime/utils/datmodelBuilder.ts b/packages/client/src/runtime/utils/datmodelBuilder.ts index 3692fecc91a3..035db653ef04 100644 --- a/packages/client/src/runtime/utils/datmodelBuilder.ts +++ b/packages/client/src/runtime/utils/datmodelBuilder.ts @@ -1,6 +1,6 @@ import { DMMF } from '@prisma/generator-helper' -import { BaseDMMFHelper } from '../dmmf' +import { DMMFDatamodelHelper } from '../dmmf' export function field(kind: DMMF.FieldKind, name: string, type: string, extra?: Partial): DMMF.Field { return { @@ -38,18 +38,11 @@ export function model(name: string, fields: DMMF.Field[]): DMMF.Model { } export function baseDmmf({ models }: { models: DMMF.Model[] }) { - return new BaseDMMFHelper({ + return new DMMFDatamodelHelper({ datamodel: { models, enums: [], types: [], }, - mappings: { - modelOperations: [], - otherOperations: { - read: [], - write: [], - }, - }, }) } diff --git a/packages/client/tests/functional/unsupported-action/_matrix.ts b/packages/client/tests/functional/unsupported-action/_matrix.ts new file mode 100644 index 000000000000..d48aae47e031 --- /dev/null +++ b/packages/client/tests/functional/unsupported-action/_matrix.ts @@ -0,0 +1,21 @@ +import { defineMatrix } from '../_utils/defineMatrix' + +export default defineMatrix(() => [ + [ + { + provider: 'sqlite', + }, + { + provider: 'postgresql', + }, + { + provider: 'mysql', + }, + { + provider: 'cockroachdb', + }, + { + provider: 'sqlserver', + }, + ], +]) diff --git a/packages/client/tests/functional/unsupported-action/prisma/_schema.ts b/packages/client/tests/functional/unsupported-action/prisma/_schema.ts new file mode 100644 index 000000000000..6b7bf6280bde --- /dev/null +++ b/packages/client/tests/functional/unsupported-action/prisma/_schema.ts @@ -0,0 +1,19 @@ +import { idForProvider } from '../../_utils/idForProvider' +import testMatrix from '../_matrix' + +export default testMatrix.setupSchema(({ provider }) => { + return /* Prisma */ ` + generator client { + provider = "prisma-client-js" + } + + datasource db { + provider = "${provider}" + url = env("DATABASE_URI_${provider}") + } + + model User { + id ${idForProvider(provider)} + } + ` +}) diff --git a/packages/client/tests/functional/unsupported-action/tests.ts b/packages/client/tests/functional/unsupported-action/tests.ts new file mode 100644 index 000000000000..cbc2376d5020 --- /dev/null +++ b/packages/client/tests/functional/unsupported-action/tests.ts @@ -0,0 +1,53 @@ +import { getQueryEngineProtocol } from '@prisma/internals' + +import testMatrix from './_matrix' +// @ts-ignore +import type { PrismaClient } from './node_modules/@prisma/client' + +declare let prisma: PrismaClient + +testMatrix.setupTestSuite( + () => { + testIf(getQueryEngineProtocol() === 'graphql')('unsupported method (graphql)', async () => { + // @ts-expect-error + const result = prisma.user.aggregateRaw() + await expect(result).rejects.toMatchPrismaErrorInlineSnapshot(` + + Invalid \`prisma.user.aggregateRaw()\` invocation in + /client/tests/functional/unsupported-action/tests.ts:0:0 + + XX () => { + XX testIf(getQueryEngineProtocol() === 'graphql')('unsupported method (graphql)', async () => { + XX // @ts-expect-error + → XX const result = prisma.user.aggregateRaw( + Model \`User\` does not support \`aggregateRaw\` action. + `) + }) + + testIf(getQueryEngineProtocol() === 'json')('unsupported method (json)', async () => { + // @ts-expect-error + const result = prisma.user.aggregateRaw() + await expect(result).rejects.toMatchPrismaErrorInlineSnapshot(` + + Invalid \`prisma.user.aggregateRaw()\` invocation in + /client/tests/functional/unsupported-action/tests.ts:0:0 + + XX + XX testIf(getQueryEngineProtocol() === 'json')('unsupported method (json)', async () => { + XX // @ts-expect-error + → XX const result = prisma.user.aggregateRaw( + Operation 'aggregateRaw' for model 'User' does not match any query. + `) + }) + }, + { + skipDataProxy: { + runtimes: ['edge'], + reason: 'Error rendering is different for edge client', + }, + optOut: { + from: ['mongodb'], + reason: 'Test uses aggregateRaw as an example of unsupported method for SQL databases, it exists on mongo', + }, + }, +)