Skip to content

Commit

Permalink
progress
Browse files Browse the repository at this point in the history
  • Loading branch information
enisdenjo committed Dec 19, 2022
1 parent bb2a64f commit 6951799
Show file tree
Hide file tree
Showing 11 changed files with 181 additions and 184 deletions.
73 changes: 33 additions & 40 deletions packages/cli/src/codegen/schema.js
@@ -1,13 +1,12 @@
const tsCodegen = require('./typescript')
const typesCodegen = require('./types')


class IdField {
static BYTES = Symbol('Bytes')
static STRING = Symbol('String')

constructor(idField) {
const typeName = idField.getIn(['type', 'type', 'name', 'value'])
const typeName = idField.type?.type?.name?.value
this.kind = typeName === 'Bytes' ? IdField.BYTES : IdField.STRING
}

Expand Down Expand Up @@ -40,12 +39,12 @@ class IdField {
}

static fromFields(fields) {
const idField = fields.find(field => field.getIn(['name', 'value']) === 'id')
const idField = fields.find(field => field.name?.value === 'id')
return new IdField(idField)
}

static fromTypeDef(def) {
return IdField.fromFields(def.get('fields'))
return IdField.fromFields(def.fields)
}
}

Expand Down Expand Up @@ -78,29 +77,26 @@ module.exports = class SchemaCodeGenerator {
}

generateTypes() {
return this.schema.ast
.get('definitions')
return this.schema.ast.definitions
.filter(def => this._isEntityTypeDefinition(def))
.map(def => this._generateEntityType(def))
}

_isEntityTypeDefinition(def) {
return (
def.get('kind') === 'ObjectTypeDefinition' &&
def
.get('directives')
.find(directive => directive.getIn(['name', 'value']) === 'entity') !== undefined
def.kind === 'ObjectTypeDefinition' &&
def.directives.find(directive => directive.name?.value === 'entity') !== undefined
)
}

_isInterfaceDefinition(def) {
return def.get('kind') === 'InterfaceTypeDefinition'
return def.kind === 'InterfaceTypeDefinition'
}

_generateEntityType(def) {
let name = def.getIn(['name', 'value'])
let name = def.name?.value
let klass = tsCodegen.klass(name, { export: true, extends: 'Entity' })
const fields = def.get('fields')
const fields = def.fields
const idField = IdField.fromFields(fields)

// Generate and add a constructor
Expand All @@ -110,8 +106,7 @@ module.exports = class SchemaCodeGenerator {
this._generateStoreMethods(name, idField).forEach(method => klass.addMethod(method))

// Generate and add entity field getters and setters
def
.get('fields')
def.fields
.reduce(
(methods, field) => methods.concat(this._generateEntityFieldMethods(def, field)),
[],
Expand Down Expand Up @@ -170,8 +165,8 @@ module.exports = class SchemaCodeGenerator {
}

_generateEntityFieldGetter(entityDef, fieldDef) {
let name = fieldDef.getIn(['name', 'value'])
let gqlType = fieldDef.get('type')
let name = fieldDef.name?.value
let gqlType = fieldDef.type
let fieldValueType = this._valueTypeFromGraphQl(gqlType)
let returnType = this._typeFromGraphQl(gqlType)
let isNullable = returnType instanceof tsCodegen.NullableType
Expand All @@ -195,8 +190,8 @@ module.exports = class SchemaCodeGenerator {
}

_generateEntityFieldSetter(entityDef, fieldDef) {
let name = fieldDef.getIn(['name', 'value'])
let gqlType = fieldDef.get('type')
let name = fieldDef.name?.value
let gqlType = fieldDef.type
let fieldValueType = this._valueTypeFromGraphQl(gqlType)
let paramType = this._typeFromGraphQl(gqlType)
let isNullable = paramType instanceof tsCodegen.NullableType
Expand Down Expand Up @@ -235,17 +230,15 @@ Suggestion: add an '!' to the member type of the List, change from '[${baseType}
}

_resolveFieldType(gqlType) {
let typeName = gqlType.getIn(['name', 'value'])
let typeName = gqlType.name?.value

// If this is a reference to another type, the field has the type of
// the referred type's id field
const typeDef = this.schema.ast
.get('definitions')
.find(
def =>
(this._isEntityTypeDefinition(def) || this._isInterfaceDefinition(def)) &&
def.getIn(['name', 'value']) === typeName,
)
const typeDef = this.schema.ast.definitions.find(
def =>
(this._isEntityTypeDefinition(def) || this._isInterfaceDefinition(def)) &&
def.name?.value === typeName,
)
if (typeDef) {
return IdField.fromTypeDef(typeDef).typeName()
} else {
Expand All @@ -258,10 +251,10 @@ Suggestion: add an '!' to the member type of the List, change from '[${baseType}
* other entity types, this is the same as the type of the id of the
* referred type, i.e., `string` or `Bytes`*/
_valueTypeFromGraphQl(gqlType) {
if (gqlType.get('kind') === 'NonNullType') {
return this._valueTypeFromGraphQl(gqlType.get('type'), false)
} else if (gqlType.get('kind') === 'ListType') {
return '[' + this._valueTypeFromGraphQl(gqlType.get('type')) + ']'
if (gqlType.kind === 'NonNullType') {
return this._valueTypeFromGraphQl(gqlType.type, false)
} else if (gqlType.kind === 'ListType') {
return '[' + this._valueTypeFromGraphQl(gqlType.type) + ']'
} else {
return this._resolveFieldType(gqlType)
}
Expand All @@ -270,20 +263,20 @@ Suggestion: add an '!' to the member type of the List, change from '[${baseType}
/** Determine the base type of `gqlType` by removing any non-null
* constraints and using the type of elements of lists */
_baseType(gqlType) {
if (gqlType.get('kind') === 'NonNullType') {
return this._baseType(gqlType.get('type'))
} else if (gqlType.get('kind') === 'ListType') {
return this._baseType(gqlType.get('type'))
if (gqlType.kind === 'NonNullType') {
return this._baseType(gqlType.type)
} else if (gqlType.kind === 'ListType') {
return this._baseType(gqlType.type)
} else {
return gqlType.getIn(['name', 'value'])
return gqlType.name?.value
}
}

_typeFromGraphQl(gqlType, nullable = true) {
if (gqlType.get('kind') === 'NonNullType') {
return this._typeFromGraphQl(gqlType.get('type'), false)
} else if (gqlType.get('kind') === 'ListType') {
let type = tsCodegen.arrayType(this._typeFromGraphQl(gqlType.get('type')))
if (gqlType.kind === 'NonNullType') {
return this._typeFromGraphQl(gqlType.type, false)
} else if (gqlType.kind === 'ListType') {
let type = tsCodegen.arrayType(this._typeFromGraphQl(gqlType.type))
return nullable ? tsCodegen.nullableType(type) : type
} else {
// NamedType
Expand Down
56 changes: 31 additions & 25 deletions packages/cli/src/codegen/schema.test.js
Expand Up @@ -11,11 +11,7 @@ const {
ArrayType,
} = require('./typescript')

const formatTS = code =>
prettier.format(
code,
{ parser: 'typescript', semi: false }
)
const formatTS = code => prettier.format(code, { parser: 'typescript', semi: false })

const createSchemaCodeGen = schema =>
new SchemaCodeGenerator({
Expand Down Expand Up @@ -60,7 +56,7 @@ describe('Schema code generator', () => {
}
`)

expect(codegen.generateTypes().size).toBe(0)
expect(codegen.generateTypes().length).toBe(0)
})

describe('Should generate correct classes for each entity', () => {
Expand Down Expand Up @@ -98,7 +94,7 @@ describe('Schema code generator', () => {
const foo = generatedTypes.find(type => type.name === 'Foo')
expect(foo).toBe(undefined)
// Account and Wallet
expect(generatedTypes.size).toBe(2)
expect(generatedTypes.length).toBe(2)
})

test('Account is an entity with the correct methods', () => {
Expand Down Expand Up @@ -247,7 +243,12 @@ describe('Schema code generator', () => {
},
{
name: 'set wallets',
params: [new Param('value', new NullableType(new ArrayType(new NamedType('string'))))],
params: [
new Param(
'value',
new NullableType(new ArrayType(new NamedType('string'))),
),
],
returnType: undefined,
body: `
if (!value) {
Expand Down Expand Up @@ -376,84 +377,89 @@ describe('Schema code generator', () => {

const generatedTypes = codegen.generateTypes()
testEntity(generatedTypes, {
name: "Task",
name: 'Task',
members: [],
methods: [
{
name: 'constructor',
params: [new Param('id', new NamedType('Bytes'))],
returnType: undefined,
body: "\n super()\n this.set('id', Value.fromBytes(id))\n "
body: "\n super()\n this.set('id', Value.fromBytes(id))\n ",
},
{
name: 'save',
params: [],
returnType: new NamedType('void'),
body: '\n' +
body:
'\n' +
" let id = this.get('id')\n" +
' assert(id != null,\n' +
" 'Cannot save Task entity without an ID')\n" +
' if (id) {\n' +
' assert(id.kind == ValueKind.BYTES,\n' +
" `Entities of type Task must have an ID of type Bytes but the id '${id.displayData()}' is of type ${id.displayKind()}`)\n" +
" store.set('Task', id.toBytes().toHexString(), this)\n" +
' }'
' }',
},
{
name: 'load',
static: true,
params: [new Param('id', new NamedType('Bytes'))],
returnType: new NullableType(new NamedType('Task')),
body: '\n' +
body:
'\n' +
" return changetype<Task | null>(store.get('Task', id.toHexString()))\n" +
' '
' ',
},
{
name: 'get id',
params: [],
returnType: new NamedType('Bytes'),
body: '\n' +
body:
'\n' +
" let value = this.get('id')\n" +
' return value!.toBytes()\n' +
' '
' ',
},
{
name: 'set id',
params: [new Param('value', new NamedType('Bytes'))],
returnType: undefined,
body: "\n this.set('id', Value.fromBytes(value))\n "
body: "\n this.set('id', Value.fromBytes(value))\n ",
},
{
name: 'get employee',
params: [],
returnType: new NamedType('Bytes'),
body: '\n' +
body:
'\n' +
" let value = this.get('employee')\n" +
' return value!.toBytes()\n' +
' '
' ',
},
{
name: 'set employee',
params: [new Param('value', new NamedType('Bytes'))],
returnType: undefined,
body: "\n this.set('employee', Value.fromBytes(value))\n "
body: "\n this.set('employee', Value.fromBytes(value))\n ",
},
{
name: 'get worker',
params: [],
returnType: new NamedType('Bytes'),
body: '\n' +
body:
'\n' +
" let value = this.get('worker')\n" +
' return value!.toBytes()\n' +
' '
' ',
},
{
name: 'set worker',
params: [new Param('value', new NamedType('Bytes'))],
returnType: undefined,
body: "\n this.set('worker', Value.fromBytes(value))\n "
}
]
body: "\n this.set('worker', Value.fromBytes(value))\n ",
},
],
})
})
})

0 comments on commit 6951799

Please sign in to comment.