Skip to content

Commit

Permalink
feat: support nullable primitives and relations
Browse files Browse the repository at this point in the history
  • Loading branch information
Valentin Palkovič committed Sep 24, 2020
1 parent a131dd1 commit 0f8de59
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 37 deletions.
51 changes: 29 additions & 22 deletions src/generator/properties.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { DMMF } from '@prisma/generator-helper'
import { JSONSchema7, JSONSchema7Definition } from 'json-schema'
import { DEFINITIONS_ROOT } from './constants'
import {
assertNever,
Expand All @@ -9,7 +8,13 @@ import {
} from './helpers'
import { ModelMetaData, PropertyMap, PropertyMetaData } from './types'

function getJSONSchemaScalar(fieldType: PrismaPrimitive): JSONSchema7['type'] {
import type {
JSONSchema7,
JSONSchema7Definition,
JSONSchema7TypeName,
} from 'json-schema'

function getJSONSchemaScalar(fieldType: PrismaPrimitive): JSONSchema7TypeName {
switch (fieldType) {
case 'String':
return 'string'
Expand All @@ -29,14 +34,17 @@ function getJSONSchemaScalar(fieldType: PrismaPrimitive): JSONSchema7['type'] {
}

function getJSONSchemaType(field: DMMF.Field): JSONSchema7['type'] {
const { isList } = field
return isScalarType(field) && !isList
? getJSONSchemaScalar(field.type)
: field.isList
? 'array'
: isEnumType(field)
? 'string'
: 'object'
const { isList, isRequired } = field
const scalarFieldType =
isScalarType(field) && !isList
? getJSONSchemaScalar(field.type)
: field.isList
? 'array'
: isEnumType(field)
? 'string'
: 'object'

return isRequired || isList ? scalarFieldType : [scalarFieldType, 'null']
}

function getFormatByDMMFType(fieldType: string): string | undefined {
Expand All @@ -48,14 +56,18 @@ function getFormatByDMMFType(fieldType: string): string | undefined {
}
}

function getItemsByDMMFType(
field: DMMF.Field,
): JSONSchema7Definition | undefined {
function getJSONSchemaForPropertyReference(field: DMMF.Field): JSONSchema7 {
const notNullable = field.isRequired || field.isList
const ref = { $ref: `${DEFINITIONS_ROOT}${field.type}` }
return notNullable ? ref : { anyOf: [ref, { type: 'null' }] }
}

function getItemsByDMMFType(field: DMMF.Field): JSONSchema7['items'] {
return (isScalarType(field) && !field.isList) || isEnumType(field)
? undefined
: isScalarType(field) && field.isList
? { type: getJSONSchemaScalar(field.type) }
: { $ref: `${DEFINITIONS_ROOT}${field.type}` }
: getJSONSchemaForPropertyReference(field)
}

function isSingleReference(field: DMMF.Field) {
Expand All @@ -73,19 +85,12 @@ function getEnumListByDMMFType(modelMetaData: ModelMetaData) {
}
}

function getJSONSchemaForPropertyReference(field: DMMF.Field) {
return {
$ref: `${DEFINITIONS_ROOT}${field.type}`,
}
}

export function getJSONSchemaProperty(modelMetaData: ModelMetaData) {
return (field: DMMF.Field): PropertyMap => {
const type = getJSONSchemaType(field)
const format = getFormatByDMMFType(field.type)
const items = getItemsByDMMFType(field)
const enumList = getEnumListByDMMFType(modelMetaData)(field)
const isReference = isSingleReference(field)

const definition: JSONSchema7Definition = {
type,
Expand All @@ -100,7 +105,9 @@ export function getJSONSchemaProperty(modelMetaData: ModelMetaData) {

return [
field.name,
isReference ? getJSONSchemaForPropertyReference(field) : definition,
isSingleReference(field)
? getJSONSchemaForPropertyReference(field)
: definition,
propertyMetaData,
]
}
Expand Down
36 changes: 21 additions & 15 deletions src/tests/generator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const datamodel = /* Prisma */ `
name String?
successorId Int?
successor User? @relation("BlogOwnerHistory", fields: [successorId], references: [id])
predecessor User? @relation("BlogOwnerHistory")
predecessor User @relation("BlogOwnerHistory")
role Role @default(USER)
posts Post[]
keywords String[]
Expand Down Expand Up @@ -45,7 +45,12 @@ describe('JSON Schema Generator', () => {
Post: {
properties: {
id: { type: 'integer' },
user: { $ref: '#/definitions/User' },
user: {
anyOf: [
{ $ref: '#/definitions/User' },
{ type: 'null' },
],
},
},
type: 'object',
},
Expand All @@ -55,26 +60,31 @@ describe('JSON Schema Generator', () => {
createdAt: { format: 'date-time', type: 'string' },
email: { type: 'string' },
id: { type: 'integer' },
is18: { type: 'boolean' },
is18: { type: ['boolean', 'null'] },
keywords: { items: { type: 'string' }, type: 'array' },
name: { type: 'string' },
name: { type: ['string', 'null'] },
posts: {
items: { $ref: '#/definitions/Post' },
type: 'array',
},
predecessor: { $ref: '#/definitions/User' },
role: { enum: ['USER', 'ADMIN'], type: 'string' },
successor: { $ref: '#/definitions/User' },
weight: { type: 'integer' },
successor: {
anyOf: [
{ $ref: '#/definitions/User' },
{ type: 'null' },
],
},
weight: { type: ['integer', 'null'] },
},
type: 'object',
},
},
type: 'object',
properties: {
user: { $ref: '#/definitions/User' },
post: { $ref: '#/definitions/Post' },
user: { $ref: '#/definitions/User' },
},
type: 'object',
})
})

Expand All @@ -88,8 +98,7 @@ describe('JSON Schema Generator', () => {
post: {
id: 0,
user: {
id: 3,
weight: 10,
id: 100,
},
},
user: {
Expand All @@ -101,7 +110,7 @@ describe('JSON Schema Generator', () => {
},
is18: true,
keywords: ['prisma2', 'json-schema', 'generator'],
name: 'Jan Uwe',
name: null,
posts: [
{
id: 4,
Expand All @@ -114,10 +123,7 @@ describe('JSON Schema Generator', () => {
id: 10,
email: 'horst@wassermann.de',
},
successor: {
id: 12,
name: 'Niels Emmerich',
},
successor: null,
role: 'USER',
weight: 10,
},
Expand Down

0 comments on commit 0f8de59

Please sign in to comment.