Skip to content
This repository has been archived by the owner on Sep 2, 2022. It is now read-only.

Commit

Permalink
Database Introspection Improvements (#4031)
Browse files Browse the repository at this point in the history
* CLI/Introspection & Datamodel:
* Remove force-renaming of ID field for V2
* Always rank ID field on top, except when given otherwise in reference model.

* CLI/Datamodel: Prettier index directive rendering.

* CLI/Datamodel:
* Render booleans without quotes
* Indices uniqe default false
* Adjust all impacted unit tests.

* CLI/Datamodel: Sequences and index types are now first level citizens.

* CLI/Introspection: Sequence support.
CLI/Datamodel: Unified interface.

* CLI/Introspection: Enabled End to End tests with postgres, mysql.

* CLI/Introspection: Postgres tests for DML v2, added CMS datamodel (for testing sequences)

* CLI/Introspection: Enabled all unit tests for mysql.

* CLI/Introspection: Re-Enabled mongo tests.

* CLI/Introspection: Fallback for finding ID field. Fixed broken cardinality on back relations.

* CLI/Introspection: Don't hide link tables which are not made by prisma.
CLI/Datamodel: isLinkTable is now a first-level citizen.

* CLI: Mitigated breaking API changes.
  • Loading branch information
ejoebstl authored and timsuchanek committed Feb 11, 2019
1 parent 600b8e6 commit 27fcf5b
Show file tree
Hide file tree
Showing 75 changed files with 15,933 additions and 3,593 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ import chalk from 'chalk'
import { Client as PGClient } from 'pg'
import { MongoClient } from 'mongodb'
import { createConnection } from 'mysql'
import { Parser, DatabaseType, Renderers, ISDL } from 'prisma-datamodel'
import {
DefaultParser,
DatabaseType,
DefaultRenderer,
ISDL,
} from 'prisma-datamodel'
import { IConnector } from 'prisma-db-introspection/dist/common/connector'
import { omit } from 'lodash'
import {
Expand Down Expand Up @@ -148,7 +153,7 @@ ${chalk.bold(

getExistingDatamodel(databaseType: DatabaseType): ISDL | null {
if (this.definition.typesString) {
const ParserInstance = Parser.create(databaseType!)
const ParserInstance = DefaultParser.create(databaseType!)
return ParserInstance.parseFromSchemaString(this.definition.typesString!)
}

Expand All @@ -172,7 +177,7 @@ ${chalk.bold(
? await introspection.getNormalizedDatamodel(existingDatamodel)
: await introspection.getDatamodel()

const renderer = Renderers.create(
const renderer = DefaultRenderer.create(
introspection.databaseType,
this.flags.prototype,
)
Expand Down
4 changes: 2 additions & 2 deletions cli/packages/prisma-cli-core/src/utils/EndpointDialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import * as path from 'path'
import * as fs from 'fs'
import { MongoClient } from 'mongodb'
import * as yaml from 'js-yaml'
import { DatabaseType, Renderers } from 'prisma-datamodel'
import { DatabaseType, DefaultRenderer } from 'prisma-datamodel'
import {
getConnectedConnectorFromCredentials,
getConnectorWithDatabase,
Expand Down Expand Up @@ -384,7 +384,7 @@ export class EndpointDialog {
)
const introspection = await connector.introspect(databaseName)
const isdl = await introspection.getDatamodel()
const renderer = Renderers.create(databaseType)
const renderer = DefaultRenderer.create(databaseType)
datamodel = renderer.render(isdl)
const tableName =
databaseType === DatabaseType.mongo ? 'Mongo collections' : 'tables'
Expand Down
63 changes: 55 additions & 8 deletions cli/packages/prisma-datamodel/__tests__/parser/directives.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import RelationalParser from '../../src/datamodel/parser/relationalParser'
import DocumentParser from '../../src/datamodel/parser/documentParser'
import { IGQLType } from '../../src/datamodel/model'
import { SdlExpect } from '../../src/test-helpers'
import { TypeIdentifiers } from '../../src/datamodel/scalar';
import { IGQLType, IdStrategy } from '../../src/datamodel/model'
import { SdlExpect } from '../../src/test-helpers'
import { TypeIdentifiers } from '../../src/datamodel/scalar'

const parsersToTest = [{ name: 'relational', instance: new RelationalParser()}, { name: 'document', instance: new DocumentParser()}]
const parsersToTest = [
{ name: 'relational', instance: new RelationalParser() },
{ name: 'document', instance: new DocumentParser() },
]

for(const parser of parsersToTest) {
for (const parser of parsersToTest) {
describe(`${parser.name} parser directive tests`, () => {
test('Parse a type with build-in directives correctly.', () => {
const model = `
Expand Down Expand Up @@ -34,7 +37,6 @@ for(const parser of parsersToTest) {
expect(mappedField.relationName).toBe('typeRelation')
})


test('Parse a type with multiple index directives correctly.', () => {
const model = `
type User @db(name: "user")
Expand All @@ -53,12 +55,36 @@ for(const parser of parsersToTest) {
const idField = SdlExpect.field(userType, 'id', true, false, 'Int', true, true)
const firstNameField = SdlExpect.field(userType, 'firstName', true, false, TypeIdentifiers.string)
const lastNameField = SdlExpect.field(userType, 'lastName', true, false, TypeIdentifiers.string)

SdlExpect.index(userType, 'NameIndex', [firstNameField, lastNameField], false)
// True is the default value
SdlExpect.index(userType, 'PrimaryIndex', [idField], true)
})

test('Parse a type with sequence directive correctly.', () => {
const model = `
type User @db(name: "user") {
id: Int! @id(strategy: SEQUENCE) @sequence(name: "pk_seq", initialValue: 2, allocationSize: 99)
createdAt: DateTime! @createdAt
updatedAt: DateTime! @updatedAt
firstName: String!
lastName: String!
}`

const { types } = parser.instance.parseFromSchemaString(model)

const userType = SdlExpect.type(types, 'User')
const idField = SdlExpect.field(userType, 'id', true, false, 'Int', true, true)
const firstNameField = SdlExpect.field(userType, 'firstName', true, false, TypeIdentifiers.string)
const lastNameField = SdlExpect.field(userType, 'lastName', true, false, TypeIdentifiers.string)

expect(idField.idStrategy).toEqual(IdStrategy.Sequence)
expect(idField.associatedSequence).not.toBeNull()
expect(idField.associatedSequence!.name).toBe('pk_seq')
expect(idField.associatedSequence!.initialValue).toBe(2)
expect(idField.associatedSequence!.allocationSize).toBe(99)
})

test('Parse a type with unknown directives correctly.', () => {
const model = `
type DirectiveUser @typeDirective(name: "database") {
Expand All @@ -80,5 +106,26 @@ for(const parser of parsersToTest) {
const mappedField = SdlExpect.field(userType, 'mappedField', true, false, 'String')
SdlExpect.directive(mappedField, { name: 'unfunnyDirective', arguments: { funny: 'false' } })
})

test('Parse a type with link table directive correctly.', () => {
const model = `type User {
id: Int! @id
lastName: String!
}
type UserToUser @linkTable {
A: User!
B: User!
}
`

const { types } = parser.instance.parseFromSchemaString(model)

const userType = SdlExpect.type(types, 'User')
expect(userType.isLinkTable).toBe(false)

const linkType = SdlExpect.type(types, 'UserToUser')
expect(linkType.isLinkTable).toBe(true)
})
})
}
}
125 changes: 76 additions & 49 deletions cli/packages/prisma-datamodel/__tests__/renderer/base.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,39 @@

import { IGQLType, IGQLField, GQLScalarField, IDirectiveInfo } from '../../src/datamodel/model'
import Renderer from '../../src/datamodel/renderer'
import Parser from '../../src/datamodel/parser'
import { DatabaseType } from '../../src/databaseType';
import { DatabaseType } from '../../src/databaseType'

const renderer = Renderer.create(DatabaseType.postgres)
const parser = Parser.create(DatabaseType.postgres)

const simpleModel =
`type User {
const simpleModel = `type User {
age: int
isAdmin: Boolean @default(value: "false")
isAdmin: Boolean @default(value: false)
name: String
nationality: String @default(value: "DE")
roles: [Int]
}`

const modelWithDirectives =
`type Test @dummyDirective(isDummy: true) {
const complicatedModel = `type User @indexes(value: [
{name: "NameIndex", fields: ["firstName", "lastName"]},
{name: "PrimaryIndex", fields: ["id"], unique: true}
]) {
id: Int! @id(strategy: SEQUENCE) @sequence(name: "test_seq", initialValue: 8, allocationSize: 100)
age: int
isAdmin: Boolean @default(value: false)
nationality: String @default(value: "DE")
roles: [Int]
firstName: String!
lastName: String!
}`

const modelWithDirectives = `type Test @dummyDirective(isDummy: true) {
test: String @relation(link: INLINE)
test2: Int @defaultValue(value: 10) @relation(name: "TestRelation")
}`

describe(`Renderer test`, () => {
test('Renderer a single type with scalars and default value correctly.', () => {

const fieldWithDefaultValue = new GQLScalarField('isAdmin', 'Boolean')
fieldWithDefaultValue.defaultValue = 'false'

Expand All @@ -40,79 +49,97 @@ describe(`Renderer test`, () => {
new GQLScalarField('age', 'int'),
fieldWithDefaultValue,
fieldWithStringDefaultValue,
listField
listField,
],
name: 'User',
isEmbedded: false,
isLinkTable: false,
isEnum: false,
indices: [],
directives: [],
comments: [],
databaseName: null
databaseName: null,
}

const res = renderer.render({
types: [model]
}, true)
const res = renderer.render(
{
types: [model],
},
true,
)

expect(res).toEqual(simpleModel)
})

test('Render directives correctly', () => {
const scalarField = new GQLScalarField('test', 'String')
scalarField.directives = [{
name: "relation",
arguments: {
link: "INLINE"
}
}]
scalarField.directives = [
{
name: 'relation',
arguments: {
link: 'INLINE',
},
},
]

const arrayField = new GQLScalarField('test2', 'Int')
arrayField.directives = [{
name: "relation",
arguments: {
name: "\"TestRelation\""
}
}, {
name: "defaultValue",
arguments: {
value: "10"
}
}]

const typeDirectives: IDirectiveInfo[] = [{
name: "dummyDirective",
arguments: {
isDummy: "true"
}
}]
arrayField.directives = [
{
name: 'relation',
arguments: {
name: '"TestRelation"',
},
},
{
name: 'defaultValue',
arguments: {
value: '10',
},
},
]

const typeDirectives: IDirectiveInfo[] = [
{
name: 'dummyDirective',
arguments: {
isDummy: 'true',
},
},
]

const type: IGQLType = {
name: "Test",
name: 'Test',
isEmbedded: false,
isLinkTable: false,
directives: typeDirectives,
isEnum: false,
fields: [
scalarField, arrayField
],
fields: [scalarField, arrayField],
comments: [],
indices: [],
databaseName: null
databaseName: null,
}



const res = renderer.render({
types: [type]
}, true)
const res = renderer.render(
{
types: [type],
},
true,
)

expect(res).toEqual(modelWithDirectives)
})

test('Render a single type consistently with the parser', () => {
const parsed = parser.parseFromSchemaString(simpleModel);
const parsed = parser.parseFromSchemaString(simpleModel)
const rendered = renderer.render(parsed, true)

expect(rendered).toEqual(simpleModel)
})

test('Render a more complicated schema consistently with the parser', () => {
const parsed = parser.parseFromSchemaString(simpleModel)
const rendered = renderer.render(parsed, true)

expect(rendered).toEqual(simpleModel)
})
})
})
24 changes: 13 additions & 11 deletions cli/packages/prisma-datamodel/__tests__/renderer/baseV2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import { dedent } from '../../src/util/util'
describe(`Renderer datamodel v2 test`, () => {
test('Render postgres special fields according to datamodel v2', () => {
const renderer = Renderer.create(DatabaseType.postgres, true)

const modelWithDirectives = dedent(`
type Test {
hasBeenCreatedAt: DateTime @createdAt
hasBeenUpdatedAt: DateTime @updatedAt
primaryId: Int @id
}`)

const field1 = new GQLScalarField('hasBeenCreatedAt', 'DateTime')
field1.isCreatedAt = true
const field2 = new GQLScalarField('hasBeenUpdatedAt', 'DateTime')
Expand All @@ -23,22 +23,24 @@ describe(`Renderer datamodel v2 test`, () => {
field3.isId = true

const type: IGQLType = {
name: "Test",
name: 'Test',
isEmbedded: false,
isLinkTable: false,
isEnum: false,
fields: [
field1, field2, field3
],
fields: [field1, field2, field3],
comments: [],
directives: [],
databaseName: null,
indices: []
indices: [],
}

const res = renderer.render({
types: [type]
}, true)
const res = renderer.render(
{
types: [type],
},
true,
)

expect(res).toEqual(modelWithDirectives)
})
})
})

0 comments on commit 27fcf5b

Please sign in to comment.