Skip to content

Commit

Permalink
cli: add derived loader
Browse files Browse the repository at this point in the history
  • Loading branch information
gusinacio committed Mar 16, 2023
1 parent 692d159 commit 2db3719
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 8 deletions.
10 changes: 3 additions & 7 deletions packages/cli/src/codegen/schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const testEntity = (generatedTypes: any[], expectedEntity: any) => {
expect(members).toStrictEqual(expectedEntity.members);

for (const expectedMethod of expectedEntity.methods) {

const method = methods.find((method: any) => method.name === expectedMethod.name);

expectedMethod.static
Expand Down Expand Up @@ -231,14 +232,9 @@ describe('Schema code generator', () => {
{
name: 'get wallets',
params: [],
returnType: new NullableType(new ArrayType(new NamedType('string'))),
returnType: new NamedType('WalletLoader'),
body: `
let value = this.get('wallets')
if (!value || value.kind == ValueKind.NULL) {
return null
} else {
return value.toStringArray()
}
return new WalletLoader("Account", this.get('id')!.toString(), "wallets")
`,
},
{
Expand Down
109 changes: 108 additions & 1 deletion packages/cli/src/codegen/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,16 @@ export default class SchemaCodeGenerator {
.map((def: any) => this._generateEntityType(def));
}

generateDerivedLoaders() {
const fields = this.schema.ast.get('definitions')
.filter((def: any) => this._isEntityTypeDefinition(def))
.map((def: any) => def.get('fields'))
.flatten(1)
.filter((def: any) => this._isDerivedField(def))
.map((def: any) => this._getTypeNameForField(def));

return [...new Set(fields)].map((typeName: any) => this._generateDerivedLoader(typeName));
}
_isEntityTypeDefinition(def: immutable.Map<any, any>) {
return (
def.get('kind') === 'ObjectTypeDefinition' &&
Expand All @@ -96,6 +106,10 @@ export default class SchemaCodeGenerator {
);
}

_isDerivedField(field: any): boolean {
return field.get('directives')
.find((directive: any) => directive.getIn(['name', 'value']) === 'derivedFrom') !== undefined;
}
_isInterfaceDefinition(def: immutable.Map<any, any>) {
return def.get('kind') === 'InterfaceTypeDefinition';
}
Expand Down Expand Up @@ -124,6 +138,39 @@ export default class SchemaCodeGenerator {
return klass;
}

_generateDerivedLoader(typeName: string): any {
// <field>Loader
const klass = tsCodegen.klass(`${typeName}Loader`, { export: true, extends: 'Entity' });

klass.addMember(tsCodegen.klassMember("_entity", "string"))
klass.addMember(tsCodegen.klassMember("_field", "string"))
klass.addMember(tsCodegen.klassMember("_id", "string"))
// Generate and add a constructor
klass.addMethod(tsCodegen.method('constructor', [tsCodegen.param('entity', 'string'), tsCodegen.param('id', 'string'), tsCodegen.param('field', 'string')], undefined, `
super();
this._entity = entity;
this._id = id;
this._field = field;
`));

// Generate load() method for the Loader
klass.addMethod(tsCodegen.method('load', [], `${typeName}[]`, `
let value = store.loadRelated(this._entity, this._id, this._field);
return changetype<${typeName}[]>(value);
`))

return klass;
}
_getTypeNameForField(gqlType: any): any {
if (gqlType.get('kind') === 'NonNullType') {
return this._getTypeNameForField(gqlType.get('type'))
} else if (gqlType.get('kind') === 'ListType') {
return this._getTypeNameForField(gqlType.get('type'))
} else {
return gqlType.getIn(['name', 'value'])
}
}

_generateConstructor(_entityName: string, fields: immutable.List<any>) {
const idField = IdField.fromFields(fields);
return tsCodegen.method(
Expand Down Expand Up @@ -172,7 +219,10 @@ export default class SchemaCodeGenerator {
]);
}

_generateEntityFieldGetter(_entityDef: any, fieldDef: immutable.Map<any, any>) {
_generateEntityFieldGetter(entityDef: any, fieldDef: immutable.Map<any, any>) {
if (this._isDerivedField(fieldDef)) {
return this._generateDerivedFieldGetter(entityDef, fieldDef);
}
const name = fieldDef.getIn(['name', 'value']);
const gqlType = fieldDef.get('type');
const fieldValueType = this._valueTypeFromGraphQl(gqlType);
Expand All @@ -196,8 +246,64 @@ export default class SchemaCodeGenerator {
`,
);
}
_generateDerivedFieldGetter(entityDef: any, fieldDef: immutable.Map<any, any>) {
let entityName = entityDef.getIn(['name', 'value']);
let name = fieldDef.getIn(['name', 'value']);
let gqlType = fieldDef.get('type');
let returnType = this._returnTypeForDervied(gqlType)
return tsCodegen.method(
`get ${name}`,
[],
returnType,
`
return new ${returnType}('${entityName}', this.get('id')!.toString(), '${name}')
`,
)
}

_returnTypeForDervied(gqlType: any): any {
if (gqlType.get('kind') === 'NonNullType') {
return this._returnTypeForDervied(gqlType.get('type'))
} else if (gqlType.get('kind') === 'ListType') {
return this._returnTypeForDervied(gqlType.get('type'))
} else {

const type = tsCodegen.namedType(
gqlType.getIn(['name', 'value']) + 'Loader'
);
return type;
}
}

_generatedEntityDerivedFieldGetter(_entityDef: any, fieldDef: immutable.Map<any, any>) {
const name = fieldDef.getIn(['name', 'value']);
const gqlType = fieldDef.get('type');
const fieldValueType = this._valueTypeFromGraphQl(gqlType);
const returnType = this._typeFromGraphQl(gqlType);
const isNullable = returnType instanceof tsCodegen.NullableType;

const getNonNullable = `return ${typesCodegen.valueToAsc('value!', fieldValueType)}`;
const getNullable = `if (!value || value.kind == ValueKind.NULL) {
return null
} else {
return ${typesCodegen.valueToAsc('value', fieldValueType)}
}`;

return tsCodegen.method(
`get ${name}`,
[],
returnType,
`
let value = this.get('${name}')
${isNullable ? getNullable : getNonNullable}
`,
);
}
_generateEntityFieldSetter(_entityDef: any, fieldDef: immutable.Map<any, any>) {
// We shouldn't have setters for derived fields
// if (this._isDerivedField(fieldDef)) {
// return;
// }
const name = fieldDef.getIn(['name', 'value']);
const gqlType = fieldDef.get('type');
const fieldValueType = this._valueTypeFromGraphQl(gqlType);
Expand Down Expand Up @@ -297,3 +403,4 @@ Suggestion: add an '!' to the member type of the List, change from '[${baseType}
return nullable && !type.isPrimitive() ? tsCodegen.nullableType(type) : type;
}
}

1 change: 1 addition & 0 deletions packages/cli/src/type-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ export default class TypeGenerator {
GENERATED_FILE_NOTE,
...codeGenerator.generateModuleImports(),
...codeGenerator.generateTypes(),
...codeGenerator.generateDerivedLoaders(),
].join('\n'),
{
parser: 'typescript',
Expand Down
1 change: 1 addition & 0 deletions packages/ts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export * from './common/value';
*/
export declare namespace store {
function get(entity: string, id: string): Entity | null;
function loadRelated(entity: string, id: string, field: string): Array<Entity>;
function set(entity: string, id: string, data: Entity): void;
function remove(entity: string, id: string): void;
}
Expand Down

0 comments on commit 2db3719

Please sign in to comment.