Skip to content

Commit 9d2bd7a

Browse files
gusinacioincrypto32
authored andcommitted
cli: add derived loader
1 parent a70ac44 commit 9d2bd7a

File tree

5 files changed

+110
-8
lines changed

5 files changed

+110
-8
lines changed

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

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import prettier from 'prettier';
33
import Schema from '../schema';
44
import SchemaCodeGenerator from './schema';
55
import {
6-
ArrayType,
76
Class,
87
Method,
98
NamedType,
@@ -29,6 +28,7 @@ const testEntity = (generatedTypes: any[], expectedEntity: any) => {
2928
expect(members).toStrictEqual(expectedEntity.members);
3029

3130
for (const expectedMethod of expectedEntity.methods) {
31+
3232
const method = methods.find((method: any) => method.name === expectedMethod.name);
3333

3434
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
@@ -273,14 +273,9 @@ describe('Schema code generator', () => {
273273
{
274274
name: 'get wallets',
275275
params: [],
276-
returnType: new NullableType(new ArrayType(new NamedType('string'))),
276+
returnType: new NamedType('WalletLoader'),
277277
body: `
278-
let value = this.get('wallets')
279-
if (!value || value.kind == ValueKind.NULL) {
280-
return null
281-
} else {
282-
return value.toStringArray()
283-
}
278+
return new WalletLoader("Account", this.get('id')!.toString(), "wallets")
284279
`,
285280
},
286281
],

packages/cli/src/codegen/schema.ts

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,13 +104,29 @@ export default class SchemaCodeGenerator {
104104
.filter(Boolean) as Array<tsCodegen.Class>;
105105
}
106106

107+
generateDerivedLoaders() {
108+
const fields = this.schema.ast.definitions
109+
.filter((def) => this._isEntityTypeDefinition(def))
110+
.flatMap((def: any) => def.fields)
111+
.filter((def: any) => this._isDerivedField(def))
112+
.map((def: any) => this._getTypeNameForField(def));
113+
114+
return [...new Set(fields)].map((typeName: any) => this._generateDerivedLoader(typeName));
115+
}
116+
107117
_isEntityTypeDefinition(def: DefinitionNode): def is ObjectTypeDefinitionNode {
108118
return (
109119
def.kind === 'ObjectTypeDefinition' &&
110120
def.directives?.find(directive => directive.name.value === 'entity') !== undefined
111121
);
112122
}
113123

124+
125+
_isDerivedField(field: any): boolean {
126+
return field.directives?.find(
127+
(directive: any) => directive.name.value === 'derivedFrom'
128+
) !== undefined
129+
}
114130
_isInterfaceDefinition(def: DefinitionNode): def is InterfaceTypeDefinitionNode {
115131
return def.kind === 'InterfaceTypeDefinition';
116132
}
@@ -138,6 +154,39 @@ export default class SchemaCodeGenerator {
138154
return klass;
139155
}
140156

157+
158+
_generateDerivedLoader(typeName: string): any {
159+
// <field>Loader
160+
const klass = tsCodegen.klass(`${typeName}Loader`, { export: true, extends: 'Entity' });
161+
162+
klass.addMember(tsCodegen.klassMember("_entity", "string"))
163+
klass.addMember(tsCodegen.klassMember("_field", "string"))
164+
klass.addMember(tsCodegen.klassMember("_id", "string"))
165+
// Generate and add a constructor
166+
klass.addMethod(tsCodegen.method('constructor', [tsCodegen.param('entity', 'string'), tsCodegen.param('id', 'string'), tsCodegen.param('field', 'string')], undefined, `
167+
super();
168+
this._entity = entity;
169+
this._id = id;
170+
this._field = field;
171+
`));
172+
173+
// Generate load() method for the Loader
174+
klass.addMethod(tsCodegen.method('load', [], `${typeName}[]`, `
175+
let value = store.loadRelated(this._entity, this._id, this._field);
176+
return changetype<${typeName}[]>(value);
177+
`))
178+
179+
return klass;
180+
}
181+
_getTypeNameForField(gqlType: any): any {
182+
if (gqlType.kind === 'NonNullType') {
183+
return this._getTypeNameForField(gqlType.type)
184+
} else if (gqlType.kind === 'ListType') {
185+
return this._getTypeNameForField(gqlType.type)
186+
} else {
187+
return gqlType.name.value
188+
}
189+
}
141190
_generateConstructor(_entityName: string, fields: readonly FieldDefinitionNode[] | undefined) {
142191
const idField = IdField.fromFields(fields);
143192
return tsCodegen.method(
@@ -206,6 +255,7 @@ export default class SchemaCodeGenerator {
206255
);
207256
}
208257

258+
209259
_generateEntityFieldGetter(_entityDef: ObjectTypeDefinitionNode, fieldDef: FieldDefinitionNode) {
210260
const name = fieldDef.name.value;
211261
const gqlType = fieldDef.type;
@@ -240,7 +290,60 @@ export default class SchemaCodeGenerator {
240290
`,
241291
);
242292
}
293+
_generateDerivedFieldGetter(entityDef: any, fieldDef: FieldDefinitionNode) {
294+
let entityName = entityDef.name.value;
295+
let name = fieldDef.name.value;
296+
let gqlType = fieldDef.type;
297+
let returnType = this._returnTypeForDervied(gqlType)
298+
return tsCodegen.method(
299+
`get ${name}`,
300+
[],
301+
returnType,
302+
`
303+
return new ${returnType}('${entityName}', this.get('id')!.toString(), '${name}')
304+
`,
305+
)
306+
}
307+
243308

309+
_returnTypeForDervied(gqlType: any): any {
310+
if (gqlType.kind === 'NonNullType') {
311+
return this._returnTypeForDervied(gqlType.type)
312+
} else if (gqlType.kind === 'ListType') {
313+
return this._returnTypeForDervied(gqlType.type)
314+
} else {
315+
316+
const type = tsCodegen.namedType(
317+
gqlType.name.value + 'Loader'
318+
);
319+
return type;
320+
}
321+
}
322+
323+
_generatedEntityDerivedFieldGetter(_entityDef: any, fieldDef: FieldDefinitionNode) {
324+
const name = fieldDef.name.value;
325+
const gqlType = fieldDef.type;
326+
const fieldValueType = this._valueTypeFromGraphQl(gqlType);
327+
const returnType = this._typeFromGraphQl(gqlType);
328+
const isNullable = returnType instanceof tsCodegen.NullableType;
329+
330+
const getNonNullable = `return ${typesCodegen.valueToAsc('value!', fieldValueType)}`;
331+
const getNullable = `if (!value || value.kind == ValueKind.NULL) {
332+
return null
333+
} else {
334+
return ${typesCodegen.valueToAsc('value', fieldValueType)}
335+
}`;
336+
337+
return tsCodegen.method(
338+
`get ${name}`,
339+
[],
340+
returnType,
341+
`
342+
let value = this.get('${name}')
343+
${isNullable ? getNullable : getNonNullable}
344+
`,
345+
);
346+
}
244347
_generateEntityFieldSetter(_entityDef: ObjectTypeDefinitionNode, fieldDef: FieldDefinitionNode) {
245348
const name = fieldDef.name.value;
246349
const isDerivedField = !!fieldDef.directives?.find(
@@ -349,3 +452,4 @@ Suggestion: add an '!' to the member type of the List, change from '[${baseType}
349452
return nullable && !type.isPrimitive() ? tsCodegen.nullableType(type) : type;
350453
}
351454
}
455+

packages/cli/src/type-generator.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ export default class TypeGenerator {
161161
GENERATED_FILE_NOTE,
162162
...codeGenerator.generateModuleImports(),
163163
...codeGenerator.generateTypes(),
164+
...codeGenerator.generateDerivedLoaders(),
164165
].join('\n'),
165166
{
166167
parser: 'typescript',
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Subproject commit cabf430abcf33abcec60405e32cd5ab779c1a2ec

packages/ts/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export declare namespace store {
2626
/** If the entity was not created in the block, this function will return null. */
2727
// Matches the host function https://github.com/graphprotocol/graph-node/blob/9f4a1821146b18f6f49165305e9a8c0795120fad/runtime/wasm/src/module/mod.rs#L1091-L1099
2828
function get_in_block(entity: string, id: string): Entity | null;
29+
function loadRelated(entity: string, id: string, field: string): Array<Entity>;
2930
function set(entity: string, id: string, data: Entity): void;
3031
function remove(entity: string, id: string): void;
3132
}

0 commit comments

Comments
 (0)