Skip to content

Commit 30f2aa5

Browse files
authored
generate types for mappings when int8 is used as id (#1934)
* fix: generate types for mappings when int8 is used as id * add changeset * update changeset * fix: add Int8 to imports when codegen * fix: lint fix * fix: include INT8 in IdField kind definition * feat: add fromI64Array method to Value class for converting i64 arrays * test: add test case for Int8 as id
1 parent 9c69575 commit 30f2aa5

File tree

4 files changed

+218
-8
lines changed

4 files changed

+218
-8
lines changed

.changeset/new-rules-bake.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@graphprotocol/graph-cli": patch
3+
---
4+
5+
fix: generate types for mappings when int8 is used as id

packages/cli/src/codegen/schema.test.ts

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,134 @@ describe('Schema code generator', { concurrent: true }, () => {
583583
});
584584
});
585585

586+
test('Should handle references with Int8 id types', async () => {
587+
const codegen = createSchemaCodeGen(`
588+
interface Employee {
589+
id: Int8!
590+
name: String!
591+
}
592+
593+
type Worker implements Employee @entity {
594+
id: Int8!
595+
name: String!
596+
tasks: [Task!]
597+
}
598+
599+
type Task @entity {
600+
id: Int8!
601+
employee: Employee!
602+
workers: [Worker!] @derivedFrom(field: "tasks")
603+
worker: Worker!
604+
}
605+
`);
606+
607+
const generatedTypes = codegen.generateTypes();
608+
await testEntity(generatedTypes, {
609+
name: 'Task',
610+
members: [],
611+
methods: [
612+
{
613+
name: 'constructor',
614+
params: [new Param('id', new NamedType('Int8'))],
615+
returnType: undefined,
616+
body: "\n super()\n this.set('id', Value.fromI64(id))\n ",
617+
},
618+
{
619+
name: 'save',
620+
params: [],
621+
returnType: new NamedType('void'),
622+
body:
623+
'\n' +
624+
" let id = this.get('id')\n" +
625+
' assert(id != null,\n' +
626+
" 'Cannot save Task entity without an ID')\n" +
627+
' if (id) {\n' +
628+
' assert(id.kind == ValueKind.INT8,\n' +
629+
" `Entities of type Task must have an ID of type Int8 but the id '${id.displayData()}' is of type ${id.displayKind()}`)\n" +
630+
" store.set('Task', id.toI64().toString(), this)\n" +
631+
' }',
632+
},
633+
{
634+
name: 'loadInBlock',
635+
static: true,
636+
params: [new Param('id', new NamedType('Int8'))],
637+
returnType: new NullableType(new NamedType('Task')),
638+
body:
639+
'\n' +
640+
" return changetype<Task | null>(store.get_in_block('Task', id.toString()))\n" +
641+
' ',
642+
},
643+
{
644+
name: 'load',
645+
static: true,
646+
params: [new Param('id', new NamedType('Int8'))],
647+
returnType: new NullableType(new NamedType('Task')),
648+
body:
649+
'\n' +
650+
" return changetype<Task | null>(store.get('Task', id.toString()))\n" +
651+
' ',
652+
},
653+
{
654+
name: 'get id',
655+
params: [],
656+
returnType: new NamedType('i64'),
657+
body: `let value = this.get("id")
658+
if (!value || value.kind == ValueKind.NULL) {
659+
return 0;
660+
} else {
661+
return value.toI64()
662+
}`,
663+
},
664+
{
665+
name: 'set id',
666+
params: [new Param('value', new NamedType('i64'))],
667+
returnType: undefined,
668+
body: "\n this.set('id', Value.fromI64(value))\n ",
669+
},
670+
{
671+
name: 'get employee',
672+
params: [],
673+
returnType: new NamedType('i64'),
674+
body: `let value = this.get('employee')
675+
if (!value || value.kind == ValueKind.NULL) {
676+
return 0;
677+
} else {
678+
return value.toI64()
679+
}`,
680+
},
681+
{
682+
name: 'set employee',
683+
params: [new Param('value', new NamedType('i64'))],
684+
returnType: undefined,
685+
body: "\n this.set('employee', Value.fromI64(value))\n ",
686+
},
687+
{
688+
name: 'get worker',
689+
params: [],
690+
returnType: new NamedType('i64'),
691+
body: `let value = this.get('worker')
692+
if (!value || value.kind == ValueKind.NULL) {
693+
return 0;
694+
} else {
695+
return value.toI64()
696+
}`,
697+
},
698+
{
699+
name: 'set worker',
700+
params: [new Param('value', new NamedType('i64'))],
701+
returnType: undefined,
702+
body: "\n this.set('worker', Value.fromI64(value))\n ",
703+
},
704+
{
705+
name: 'get workers',
706+
params: [],
707+
returnType: new NamedType('WorkerLoader'),
708+
body: "\n return new WorkerLoader('Task', this.get('id')!.toString(), 'workers')\n ",
709+
},
710+
],
711+
});
712+
});
713+
586714
test('get related method for WithBytes entity', async () => {
587715
const codegen = createSchemaCodeGen(`
588716
type WithBytes @entity {

packages/cli/src/codegen/schema.ts

Lines changed: 77 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ import * as util from './util.js';
1616
class IdField {
1717
static BYTES = Symbol('Bytes');
1818
static STRING = Symbol('String');
19+
static INT8 = Symbol('Int8');
1920

20-
private kind: typeof IdField.BYTES | typeof IdField.STRING;
21+
private kind: typeof IdField.BYTES | typeof IdField.INT8 | typeof IdField.STRING;
2122

2223
constructor(idField: FieldDefinitionNode | undefined) {
2324
if (idField?.type.kind !== 'NonNullType') {
@@ -27,35 +28,102 @@ class IdField {
2728
throw Error('id field must be a named type');
2829
}
2930
const typeName = idField.type.type.name.value;
30-
this.kind = typeName === 'Bytes' ? IdField.BYTES : IdField.STRING;
31+
switch (typeName) {
32+
case 'Bytes':
33+
this.kind = IdField.BYTES;
34+
break;
35+
case 'Int8':
36+
this.kind = IdField.INT8;
37+
break;
38+
case 'String':
39+
this.kind = IdField.STRING;
40+
break;
41+
default:
42+
this.kind = IdField.STRING;
43+
break;
44+
}
3145
}
3246

3347
typeName() {
34-
return this.kind === IdField.BYTES ? 'Bytes' : 'string';
48+
switch (this.kind) {
49+
case IdField.BYTES:
50+
return 'Bytes';
51+
case IdField.INT8:
52+
return 'Int8';
53+
case IdField.STRING:
54+
return 'string';
55+
default:
56+
return 'string';
57+
}
3558
}
3659

3760
gqlTypeName() {
38-
return this.kind === IdField.BYTES ? 'Bytes' : 'String';
61+
switch (this.kind) {
62+
case IdField.BYTES:
63+
return 'Bytes';
64+
case IdField.INT8:
65+
return 'Int8';
66+
case IdField.STRING:
67+
return 'String';
68+
default:
69+
return 'String';
70+
}
3971
}
4072

4173
tsNamedType() {
4274
return tsCodegen.namedType(this.typeName());
4375
}
4476

4577
tsValueFrom() {
46-
return this.kind === IdField.BYTES ? 'Value.fromBytes(id)' : 'Value.fromString(id)';
78+
switch (this.kind) {
79+
case IdField.BYTES:
80+
return 'Value.fromBytes(id)';
81+
case IdField.INT8:
82+
return 'Value.fromI64(id)';
83+
case IdField.STRING:
84+
return 'Value.fromString(id)';
85+
default:
86+
return 'Value.fromString(id)';
87+
}
4788
}
4889

4990
tsValueKind() {
50-
return this.kind === IdField.BYTES ? 'ValueKind.BYTES' : 'ValueKind.STRING';
91+
switch (this.kind) {
92+
case IdField.BYTES:
93+
return 'ValueKind.BYTES';
94+
case IdField.INT8:
95+
return 'ValueKind.INT8';
96+
case IdField.STRING:
97+
return 'ValueKind.STRING';
98+
default:
99+
return 'ValueKind.STRING';
100+
}
51101
}
52102

53103
tsValueToString() {
54-
return this.kind == IdField.BYTES ? 'id.toBytes().toHexString()' : 'id.toString()';
104+
switch (this.kind) {
105+
case IdField.BYTES:
106+
return 'id.toBytes().toHexString()';
107+
case IdField.INT8:
108+
return 'id.toI64().toString()';
109+
case IdField.STRING:
110+
return 'id.toString()';
111+
default:
112+
return 'id.toString()';
113+
}
55114
}
56115

57116
tsToString() {
58-
return this.kind == IdField.BYTES ? 'id.toHexString()' : 'id';
117+
switch (this.kind) {
118+
case IdField.BYTES:
119+
return 'id.toHexString()';
120+
case IdField.INT8:
121+
return 'id.toString()';
122+
case IdField.STRING:
123+
return 'id';
124+
default:
125+
return 'id';
126+
}
59127
}
60128

61129
static fromFields(fields: readonly FieldDefinitionNode[] | undefined) {
@@ -92,6 +160,7 @@ export default class SchemaCodeGenerator {
92160
'Bytes',
93161
'BigInt',
94162
'BigDecimal',
163+
'Int8',
95164
],
96165
'@graphprotocol/graph-ts',
97166
),

packages/ts/common/value.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,14 @@ export class Value {
339339
return Value.fromArray(output);
340340
}
341341

342+
static fromI64Array(input: Array<i64>): Value {
343+
const output = new Array<Value>(input.length);
344+
for (let i: i64 = 0; i < input.length; i++) {
345+
output[i] = Value.fromI64(input[i]);
346+
}
347+
return Value.fromArray(output);
348+
}
349+
342350
static fromBigIntArray(input: Array<BigInt>): Value {
343351
const output = new Array<Value>(input.length);
344352
for (let i: i32 = 0; i < input.length; i++) {

0 commit comments

Comments
 (0)