Skip to content

Commit

Permalink
incorporate introspection engine changes
Browse files Browse the repository at this point in the history
  • Loading branch information
timsuchanek committed Feb 12, 2020
1 parent 3398aa3 commit 792d29e
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 120 deletions.
140 changes: 39 additions & 101 deletions cli/introspection/src/commands/Introspect.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,7 @@
import { Command, format, HelpError, getSchemaPath, arg } from '@prisma/cli'
import chalk from 'chalk'
import path from 'path'
import {
getConfig,
IntrospectionEngine,
getDMMF,
dmmfToDml,
uriToCredentials,
ConfigMetaFormat,
RustPanic,
ErrorArea,
} from '@prisma/sdk'
import { IntrospectionEngine, uriToCredentials, ConfigMetaFormat, RustPanic, ErrorArea } from '@prisma/sdk'
import { formatms } from '../util/formatms'
import fs from 'fs'
import { DataSource } from '@prisma/generator-helper'
Expand Down Expand Up @@ -45,6 +36,18 @@ export class Introspect implements Command {
`)
private constructor() {}
private printUrlAsDatasource(url: string): string {
const connectorType = databaseTypeToConnectorType(uriToCredentials(url).type)

return printDatasources([
{
config: {},
connectorType,
name: 'db',
url,
},
])
}

// parse arguments
public async parse(argv: string[], minimalOutput = false): Promise<string | Error> {
Expand Down Expand Up @@ -72,30 +75,19 @@ export class Introspect implements Command {

let url: string | undefined = args['--url']
let schemaPath = await getSchemaPath(args['--schema'])
let config: ConfigMetaFormat | undefined
if (!url) {
if (!schemaPath) {
throw new Error(
`Either provide ${chalk.greenBright(
'--schema',
)} or make sure that you are in a folder with a ${chalk.greenBright('schema.prisma')} file.`,
)
}

config = await getConfig({
datamodelPath: schemaPath,
})

const datasource = config.datasources[0]
if (!datasource) {
throw new Error(
`Either provide ${chalk.greenBright('--url')} or add a ${chalk.greenBright.bold(
'datasource',
)} in the ${chalk.greenBright(path.relative(process.cwd(), schemaPath))} file.`,
)
}
url = datasource.url.value
if (!url && !schemaPath) {
throw new Error(
`Either provide ${chalk.greenBright(
'--schema',
)} or make sure that you are in a folder with a ${chalk.greenBright('schema.prisma')} file.`,
)
}
// TS at its limits 🤷‍♀️
const schema: string = url
? this.printUrlAsDatasource(url)
: schemaPath
? fs.readFileSync(schemaPath, 'utf-8')
: undefined!

const engine = new IntrospectionEngine({
cwd: schemaPath ? path.dirname(schemaPath) : undefined,
Expand All @@ -109,88 +101,34 @@ export class Introspect implements Command {

const before = Date.now()
let introspectionSchema = ''
introspectionSchema = await engine.introspect(url)
introspectionSchema = await engine.introspect(schema)

if (introspectionSchema.trim() === '') {
throw new Error(`${chalk.red.bold('The introspected database was empty:')} ${chalk.underline(url)}
${chalk.bold('prisma2 introspect')} could not create any models in your ${chalk.bold('schema.prisma')} file and you will not be able to generate Prisma Client with the ${chalk.bold('prisma2 generate')} command.
${chalk.bold('prisma2 introspect')} could not create any models in your ${chalk.bold(
'schema.prisma',
)} file and you will not be able to generate Prisma Client with the ${chalk.bold('prisma2 generate')} command.
${chalk.bold('To fix this, you have two options:')}
- manually create a table in your database (using SQL).
- make sure the database connection URL inside the ${chalk.bold('datasource')} block in ${chalk.bold('schema.prisma')} points to a database that is not empty (it must contain at least one table).
- make sure the database connection URL inside the ${chalk.bold('datasource')} block in ${chalk.bold(
'schema.prisma',
)} points to a database that is not empty (it must contain at least one table).
Then you can run ${chalk.green('prisma2 introspect')} again.
`)
}

const connectorType = databaseTypeToConnectorType(uriToCredentials(url).type)
log(`Done with introspection in ${chalk.bold(formatms(Date.now() - before))}`)

const datasourceString = printDatasources([
{
config: {},
connectorType,
name: 'db',
url,
},
])

introspectionSchema = datasourceString + '\n' + introspectionSchema

debug('introspectionSchema:')
debug(introspectionSchema)

try {
const dmmf = await getDMMF({ datamodel: introspectionSchema })

// add the datasource itself to the schema in case no schema.prisma exists yet
const datasources: DataSource[] = [
{
name: 'db',
config: {},
connectorType,
url: {
value: url,
fromEnvVar: null,
},
},
]
const schema = await dmmfToDml({
config: config || {
datasources,
generators: [],
},
dmmf: dmmf.datamodel,
})

log(`Done with introspection in ${chalk.bold(formatms(Date.now() - before))}`)

if (args['--print']) {
console.log(schema)
} else {
schemaPath = schemaPath || 'schema.prisma'
fs.writeFileSync(schemaPath, schema)
log(`Wrote ${chalk.underline(path.relative(process.cwd(), schemaPath))}`)
}
} catch (e) {
engine.stop()

console.error(chalk.bold.red(`\nIntrospection failed:`) + chalk.red(` Introspected schema can't be parsed.`))
if (introspectionSchema) {
console.log(chalk.bold(`Introspected Schema:\n`))
console.log(introspectionSchema + '\n')
}

throw new RustPanic(
stripAnsi(e.message),
stripAnsi(e.message),
{},
ErrorArea.INTROSPECTION_CLI,
undefined,
introspectionSchema,
url,
)
if (args['--print']) {
console.log(introspectionSchema)
} else {
schemaPath = schemaPath || 'schema.prisma'
fs.writeFileSync(schemaPath, introspectionSchema)
log(`Wrote ${chalk.underline(path.relative(process.cwd(), schemaPath))}`)
}

engine.stop()
Expand Down
3 changes: 2 additions & 1 deletion cli/sdk/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ dmmf.json
introspection-engine*
migration-engine*
write-test
sandbox
sandbox
failed-*
24 changes: 13 additions & 11 deletions cli/sdk/src/IntrospectionEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,24 +82,26 @@ export class IntrospectionEngine {
) {
this.listeners[id] = callback
}
public getDatabaseDescription(url: string): Promise<string> {
public getDatabaseDescription(schema: string): Promise<string> {
return this.runCommand(
this.getRPCPayload('getDatabaseDescription', { url }),
this.getRPCPayload('getDatabaseDescription', { schema }),
)
}
public introspect(url: string): Promise<string> {
this.lastUrl = url
return this.runCommand(this.getRPCPayload('introspect', { url }))
public introspect(schema: string): Promise<string> {
this.lastUrl = schema
return this.runCommand(this.getRPCPayload('introspect', { schema }))
}
public listDatabases(url: string): Promise<string[]> {
this.lastUrl = url
return this.runCommand(this.getRPCPayload('listDatabases', { url }))
public listDatabases(schema: string): Promise<string[]> {
this.lastUrl = schema
return this.runCommand(this.getRPCPayload('listDatabases', { schema }))
}
public getDatabaseMetadata(
url: string,
schema: string,
): Promise<{ size_in_bytes: number; table_count: number }> {
this.lastUrl = url
return this.runCommand(this.getRPCPayload('getDatabaseMetadata', { url }))
this.lastUrl = schema
return this.runCommand(
this.getRPCPayload('getDatabaseMetadata', { schema }),
)
}
private handleResponse(response: any) {
let result
Expand Down
23 changes: 16 additions & 7 deletions cli/sdk/src/__tests__/introspection/introspection.test.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
import { IntrospectionEngine } from '../../IntrospectionEngine'
import path from 'path'

test('basic introspection', async () => {
const engine = new IntrospectionEngine({
cwd: __dirname,
})

const url = `file:${path.resolve(__dirname, 'blog.db')}`
const url = `file:./blog.db`

const result = await engine.introspect(url)
const schema = `datasource ds {
provider = "sqlite"
url = "${url}"
}`

const result = await engine.introspect(schema)
expect(result).toMatchInlineSnapshot(`
"model User {
"datasource ds {
provider = \\"sqlite\\"
url = \\"file:./blog.db\\"
}
model User {
age Int @default(0)
amount Float @default(0)
balance Float @default(0)
Expand All @@ -32,21 +41,21 @@ test('basic introspection', async () => {
author User
}"
`)
const metadata = await engine.getDatabaseMetadata(url)
const metadata = await engine.getDatabaseMetadata(schema)
expect(metadata).toMatchInlineSnapshot(`
Object {
"size_in_bytes": 0,
"table_count": 3,
}
`)
const databases = await engine.listDatabases(url)
const databases = await engine.listDatabases(schema)
expect(databases).toMatchInlineSnapshot(`
Array [
"",
"blog.db",
]
`)
const description = await engine.getDatabaseDescription(url)
const description = await engine.getDatabaseDescription(schema)
expect(JSON.parse(description)).toMatchInlineSnapshot(`
Object {
"enums": Array [],
Expand Down

0 comments on commit 792d29e

Please sign in to comment.