Skip to content

Commit

Permalink
feat(validate): add lint warnings (#16458)
Browse files Browse the repository at this point in the history
Co-authored-by: Jan Piotrowski <piotrowski+github@gmail.com>
  • Loading branch information
Jolg42 and janpio committed Nov 28, 2022
1 parent 8509fbb commit 8d8a1ed
Show file tree
Hide file tree
Showing 17 changed files with 235 additions and 51 deletions.
2 changes: 1 addition & 1 deletion packages/cli/src/Doctor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ ${chalk.bold('Examples')}

const schema = await readFile(schemaPath, 'utf-8')
const localDmmf = await getDMMF({ datamodel: schema })
const config = await getConfig({ datamodel: schema })
const config = await getConfig({ datamodel: schema, ignoreEnvVarErrors: false })

console.error(`👩‍⚕️🏥 Prisma Doctor checking the database...`)

Expand Down
6 changes: 2 additions & 4 deletions packages/cli/src/Format.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import type { Command } from '@prisma/internals'
import { arg, format, formatms, formatSchema, getDMMF, HelpError } from '@prisma/internals'
import { arg, Command, format, formatms, formatSchema, getDMMF, HelpError } from '@prisma/internals'
import { getSchemaPathAndPrint } from '@prisma/migrate'
import chalk from 'chalk'
import fs from 'fs'
import os from 'os'

/**
* $ prisma format
Expand Down Expand Up @@ -63,7 +61,7 @@ Or specify a Prisma schema path
})
} catch (e) {
console.error('') // empty line for better readability
throw new Error(`${e.message}`)
throw e
}

fs.writeFileSync(schemaPath, output)
Expand Down
42 changes: 37 additions & 5 deletions packages/cli/src/Validate.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import type { Command } from '@prisma/internals'
import { arg, format, getConfig, getDMMF, HelpError, loadEnvFile } from '@prisma/internals'
import {
arg,
format,
getConfig,
getDMMF,
getLintWarningsAsText,
handleLintPanic,
HelpError,
lintSchema,
loadEnvFile,
} from '@prisma/internals'
import { getSchemaPathAndPrint } from '@prisma/migrate'
import chalk from 'chalk'
import fs from 'fs'
Expand Down Expand Up @@ -56,13 +66,35 @@ ${chalk.bold('Examples')}

const schema = fs.readFileSync(schemaPath, 'utf-8')

// TODO is the order of getDMMF / getConfig important
await getDMMF({
datamodel: schema,
})
const { lintDiagnostics } = handleLintPanic(
() => {
// the only possible error here is a Rust panic
const lintDiagnostics = lintSchema({ schema })
return { lintDiagnostics }
},
{ schema },
)

const lintWarnings = getLintWarningsAsText(lintDiagnostics)
if (lintWarnings) {
// Output warnings to stderr
console.warn(lintWarnings)
}

try {
// Validate whether the formatted output is a valid schema
await getDMMF({
datamodel: schema,
})
} catch (e) {
console.error('') // empty line for better readability
throw e
}

// We could have a CLI flag to ignore env var validation
await getConfig({
datamodel: schema,
ignoreEnvVarErrors: false,
})

return `The schema at ${chalk.underline(schemaPath)} is valid 🚀`
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/Version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ export class Version implements Command {
const datamodel = await getSchema()
const config = await getConfig({
datamodel,
ignoreEnvVarErrors: false,
})
const generator = config.generators.find((g) => g.previewFeatures.length > 0)
if (generator) {
Expand Down
57 changes: 55 additions & 2 deletions packages/cli/src/__tests__/artificial-panic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ describeIf(process.env.PRISMA_CLI_QUERY_ENGINE_TYPE == 'library')('artificial-pa
process.env = { ...OLD_ENV }
})

it('query-engine get-dmmf library', async () => {
it('query-engine get-dmmf library - validate', async () => {
ctx.fixture('artificial-panic')
expect.assertions(5)
process.env.FORCE_PANIC_QUERY_ENGINE_GET_DMMF = '1'
Expand Down Expand Up @@ -161,6 +161,38 @@ describeIf(process.env.PRISMA_CLI_QUERY_ENGINE_TYPE == 'library')('artificial-pa
})
}
})

it('query-engine get-dmmf library - format', async () => {
ctx.fixture('artificial-panic')
expect.assertions(5)
process.env.FORCE_PANIC_QUERY_ENGINE_GET_DMMF = '1'

const command = new Format()
try {
await command.parse([])
} catch (e) {
expect(e).toMatchInlineSnapshot(`FORCE_PANIC_QUERY_ENGINE_GET_DMMF`)
expect(isRustPanic(e)).toBe(true)
expect(e.rustStack).toBeTruthy()
expect(e.schema).toMatchInlineSnapshot(`
// 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 = "postgresql"
url = env("DATABASE_URL")
}
`)
expect(e).toMatchObject({
schemaPath: undefined,
})
}
})
})

describeIf(process.env.PRISMA_CLI_QUERY_ENGINE_TYPE == 'binary')('artificial-panic binary', () => {
Expand All @@ -170,7 +202,7 @@ describeIf(process.env.PRISMA_CLI_QUERY_ENGINE_TYPE == 'binary')('artificial-pan
process.env = { ...OLD_ENV }
})

it('query-engine get-dmmf binary', async () => {
it('query-engine get-dmmf binary - validate', async () => {
ctx.fixture('artificial-panic')
expect.assertions(5)
process.env.FORCE_PANIC_QUERY_ENGINE_GET_DMMF = '1'
Expand All @@ -190,4 +222,25 @@ describeIf(process.env.PRISMA_CLI_QUERY_ENGINE_TYPE == 'binary')('artificial-pan
})
}
})

it('query-engine get-dmmf binary - format', async () => {
ctx.fixture('artificial-panic')
expect.assertions(5)
process.env.FORCE_PANIC_QUERY_ENGINE_GET_DMMF = '1'

const command = new Format()
try {
await command.parse([])
} catch (e) {
expect(e).toMatchInlineSnapshot(
`Command failed with exit code 101: prisma-engines-path FORCE_PANIC_QUERY_ENGINE_GET_DMMF`,
)
expect(isRustPanic(e)).toBe(true)
expect(e.rustStack).toBeTruthy()
expect(e.schemaPath).toBeTruthy()
expect(e).toMatchObject({
schema: undefined,
})
}
})
})
32 changes: 30 additions & 2 deletions packages/cli/src/__tests__/commands/Format.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { jestContext } from '@prisma/internals'
import { jestConsoleContext, jestContext } from '@prisma/internals'
import fs from 'fs-jetpack'

import { Format } from '../../Format'

const ctx = jestContext.new().assemble()
const ctx = jestContext.new().add(jestConsoleContext()).assemble()

it('format should add a trailing EOL', async () => {
ctx.fixture('example-project/prisma')
Expand All @@ -21,3 +21,31 @@ it('format should throw if schema is broken', async () => {
ctx.fixture('example-project/prisma')
await expect(Format.new().parse(['--schema=broken.prisma'])).rejects.toThrowError()
})

it('format should succeed and show a warning on stderr (preview feature deprecated)', async () => {
ctx.fixture('lint-warnings')
await expect(Format.new().parse(['--schema=preview-feature-deprecated.prisma'])).resolves.toBeTruthy()

// stderr
expect(ctx.mocked['console.warn'].mock.calls.join('\n')).toMatchInlineSnapshot(`
Prisma schema warning:
- Preview feature "nativeTypes" is deprecated. The functionality can be used without specifying it as a preview feature.
`)
expect(ctx.mocked['console.error'].mock.calls.join('\n')).toMatchInlineSnapshot(``)
})

it('format should throw with an error and show a warning on stderr (preview feature deprecated)', async () => {
ctx.fixture('lint-warnings')
await expect(Format.new().parse(['--schema=preview-feature-deprecated-and-error.prisma'])).rejects.toThrowError(
'P1012',
)

// stderr
expect(ctx.mocked['console.warn'].mock.calls.join('\n')).toMatchInlineSnapshot(`
Prisma schema warning:
- Preview feature "nativeTypes" is deprecated. The functionality can be used without specifying it as a preview feature.
`)
expect(ctx.mocked['console.error'].mock.calls.join('\n')).toMatchInlineSnapshot(``)
})
32 changes: 30 additions & 2 deletions packages/cli/src/__tests__/commands/Validate.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { jestContext, serializeQueryEngineName } from '@prisma/internals'
import { jestConsoleContext, jestContext, serializeQueryEngineName } from '@prisma/internals'

import { Validate } from '../../Validate'

const ctx = jestContext.new().assemble()
const ctx = jestContext.new().add(jestConsoleContext()).assemble()

it('validate should succeed if schema is valid', async () => {
ctx.fixture('example-project/prisma')
Expand All @@ -21,6 +21,34 @@ it('validate should throw if env var is not set', async () => {
)
})

it('validate should succeed and show a warning on stderr (preview feature deprecated)', async () => {
ctx.fixture('lint-warnings')
await expect(Validate.new().parse(['--schema=preview-feature-deprecated.prisma'])).resolves.toBeTruthy()

// stderr
expect(ctx.mocked['console.warn'].mock.calls.join('\n')).toMatchInlineSnapshot(`
Prisma schema warning:
- Preview feature "nativeTypes" is deprecated. The functionality can be used without specifying it as a preview feature.
`)
expect(ctx.mocked['console.error'].mock.calls.join('\n')).toMatchInlineSnapshot(``)
})

it('validate should throw with an error and show a warning on stderr (preview feature deprecated)', async () => {
ctx.fixture('lint-warnings')
await expect(Validate.new().parse(['--schema=preview-feature-deprecated-and-error.prisma'])).rejects.toThrowError(
'P1012',
)

// stderr
expect(ctx.mocked['console.warn'].mock.calls.join('\n')).toMatchInlineSnapshot(`
Prisma schema warning:
- Preview feature "nativeTypes" is deprecated. The functionality can be used without specifying it as a preview feature.
`)
expect(ctx.mocked['console.error'].mock.calls.join('\n')).toMatchInlineSnapshot(``)
})

describe('referential actions', () => {
beforeEach(() => {
ctx.fixture('referential-actions/no-action/relationMode-prisma')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
generator client {
provider = "prisma-client-js"
previewFeatures = ["nativeTypes", "does-not-exists"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
generator client {
provider = "prisma-client-js"
previewFeatures = ["nativeTypes"]
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ import { MemoryTestDir } from './MemoryTestDir'
export async function generateMemoryTestClient(testDir: MemoryTestDir) {
const schema = await fs.readFile(testDir.schemaFilePath, 'utf8')
const dmmf = await getDMMF({ datamodel: schema, datamodelPath: testDir.schemaFilePath })
const config = await getConfig({ datamodel: schema, datamodelPath: testDir.schemaFilePath })
const config = await getConfig({
datamodel: schema,
datamodelPath: testDir.schemaFilePath,
ignoreEnvVarErrors: false,
})
const generator = config.generators.find((g) => parseEnvValue(g.provider) === 'prisma-client-js')

await generateClient({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ describe('getConfig', () => {

try {
// @ts-expect-error
await getConfig({ datamodel: true })
await getConfig({ datamodel: true, ignoreEnvVarErrors: false })
} catch (e) {
const error = e as Error
expect(isRustPanic(error)).toBe(true)
Expand All @@ -16,6 +16,7 @@ describe('getConfig', () => {

test('empty config', async () => {
const config = await getConfig({
ignoreEnvVarErrors: false,
datamodel: `
datasource db {
provider = "sqlite"
Expand All @@ -37,6 +38,7 @@ describe('getConfig', () => {

test('with generator and datasource', async () => {
const config = await getConfig({
ignoreEnvVarErrors: false,
datamodel: `
datasource db {
url = "file:dev.db"
Expand Down Expand Up @@ -64,6 +66,7 @@ describe('getConfig', () => {
process.env.TEST_POSTGRES_URI_FOR_DATASOURCE = 'postgres://user:password@something:5432/db'

const config = await getConfig({
ignoreEnvVarErrors: false,
datamodel: `
datasource db {
provider = "postgresql"
Expand All @@ -90,6 +93,7 @@ describe('getConfig', () => {
})
test('with engineType="binary"', async () => {
const binaryConfig = await getConfig({
ignoreEnvVarErrors: false,
datamodel: `
datasource db {
provider = "sqlite"
Expand Down Expand Up @@ -141,6 +145,7 @@ Object {
})
test('with engineType="library"', async () => {
const libraryConfig = await getConfig({
ignoreEnvVarErrors: false,
datamodel: `
datasource db {
provider = "sqlite"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ describe('lint valid schema with a deprecated preview feature', () => {
}

test('should return a deprecated preview feature warning', () => {
const lintDiagnostics = lintSchema(schema)
const lintDiagnostics = lintSchema({ schema })

expect(ctx.mocked['console.log'].mock.calls.join('\n')).toMatchInlineSnapshot(`""`)
expect(ctx.mocked['console.warn'].mock.calls.join('\n')).toMatchInlineSnapshot(`""`)
Expand Down Expand Up @@ -83,7 +83,7 @@ Either choose another referential action, or make the referenced fields optional
}

test('should return a parsing error and a deprecated preview feature warning', () => {
const lintDiagnostics = lintSchema(schema)
const lintDiagnostics = lintSchema({ schema })

expect(ctx.mocked['console.log'].mock.calls.join('\n')).toMatchInlineSnapshot(`""`)
expect(ctx.mocked['console.warn'].mock.calls.join('\n')).toMatchInlineSnapshot(`""`)
Expand Down

0 comments on commit 8d8a1ed

Please sign in to comment.