UNPKG

@cosmology/ast

Version:
690 lines (628 loc) 20.3 kB
import * as t from '@babel/types'; import { arrowFunctionExpression, classDeclaration, classMethod, classProperty, commentBlock, identifier, tsMethodSignature } from '../../../../utils'; import { ProtoService, ProtoServiceMethod } from '@cosmology/types'; import { GenericParseContext } from '../../../../encoding'; import { camel, getServiceImplement } from '@cosmology/utils'; import { processRpcComment, returnReponseType, cleanType, optionalBool } from '../utils/rpc'; import { BinaryCoder } from '../../../../utils/binary-coder-expression'; const rpcMethodDefinition = ( name: string, svc: ProtoServiceMethod, trailingComments?: t.CommentBlock[], leadingComments?: t.CommentBlock[], ) => { const requestType = svc.requestType; const responseType = svc.responseType; const fieldNames = Object.keys(svc.fields ?? {}) const hasParams = fieldNames.length > 0; const optional = optionalBool(hasParams, fieldNames); const methodArgs: t.Identifier = identifier( 'request', t.tsTypeAnnotation( t.tsTypeReference( t.identifier(requestType) ) ), optional ); return tsMethodSignature( t.identifier(name), null, [ methodArgs ], returnReponseType(responseType), trailingComments, leadingComments ); } const rpcTxMethodDefinition = ( name: string, svc: ProtoServiceMethod, trailingComments?: t.CommentBlock[], leadingComments?: t.CommentBlock[], ) => { const requestType = svc.requestType; const responseType = t.tsTypeReference( t.identifier("DeliverTxResponse"), ); const methodArgs: t.Identifier[] = [ identifier("signerAddress", t.tsTypeAnnotation(t.tsStringKeyword())), identifier( "message", t.tsTypeAnnotation(t.tsTypeReference(t.identifier(requestType))) ), identifier( "fee", t.tsTypeAnnotation( t.tsUnionType([ t.tsNumberKeyword(), t.tsTypeReference(t.identifier("StdFee")), t.tsLiteralType(t.stringLiteral("auto")), ]) ) ), identifier("memo", t.tsTypeAnnotation(t.tsStringKeyword()), true), ]; return tsMethodSignature( t.identifier(name), null, methodArgs, returnReponseType(responseType), trailingComments, leadingComments ); } // this.Accounts = this.Accounts.bind(this); // MARKED AS NOT DRY (used in rpc/lcd) const bindThis = (name: string) => { return t.expressionStatement( t.assignmentExpression( '=', t.memberExpression( t.thisExpression(), t.identifier(name) ), t.callExpression( t.memberExpression( t.memberExpression( t.thisExpression(), t.identifier(name) ), t.identifier('bind') ), [ t.thisExpression() ] ) ) ); }; // const data = QueryAccountsRequest.encode(request).finish(); const encodeData = (name: string) => { return t.variableDeclaration( 'const', [ t.variableDeclarator( t.identifier('data'), t.callExpression( t.memberExpression( t.callExpression( t.memberExpression( t.identifier(name), t.identifier('encode') ), [ t.identifier('request') ] ), t.identifier('finish') ), [] ) ) ] ) }; // const promise = this.rpc.request("cosmos.auth.v1beta1.Query", "Accounts", data); const promiseRequest = (name: string, packageImportName: string) => { name = cleanType(name); return t.variableDeclaration( 'const', [ t.variableDeclarator( t.identifier('promise'), t.callExpression( t.memberExpression( t.memberExpression( t.thisExpression(), t.identifier('rpc') ), t.identifier('request') ), [ t.stringLiteral(packageImportName), t.stringLiteral(name), t.identifier('data') ] ) ) ] ); }; // return promise.then((data) => QueryAccountsResponse.decode(new _m0.Reader(data))); const returnPromise = (name: string, context: GenericParseContext) => { name = cleanType(name); return t.returnStatement( t.callExpression( t.memberExpression( t.identifier('promise'), t.identifier('then') ), [ t.arrowFunctionExpression( [ t.identifier('data') ], t.callExpression( t.memberExpression( t.identifier(name), t.identifier('decode') ), [ t.newExpression( BinaryCoder.getReaderMemberExp(context), [ t.identifier('data') ] ), ...(context.options.interfaces.enabled && context.options.interfaces.useUseInterfacesParams ? [ t.identifier('undefined'), t.identifier('useInterfaces') ] : []), ] ) ) ] ) ) }; const makeComment = (comment: string) => { return [{ type: 'CommentBlock', value: ` ${comment} ` }] } const rpcClassMethod = ( context: GenericParseContext, name: string, msg: string, svc: ProtoServiceMethod, packageImport: string ) => { const requestType = svc.requestType; const responseType = svc.responseType; const comment = svc.comment ?? svc.name; let methodArgs: t.Identifier | t.AssignmentPattern = identifier( 'request', t.tsTypeAnnotation( t.tsTypeReference( t.identifier(requestType) ) ) ); const fieldNames = Object.keys(svc.fields ?? {}) const hasParams = fieldNames.length > 0; // if no params, then let's default to empty object for cleaner API if (!hasParams) { methodArgs = t.assignmentPattern( methodArgs, t.objectExpression([]) ) } else if (hasParams && fieldNames.length === 1 && fieldNames.includes('pagination')) { const paginationDefaultFromPartial = context.pluginValue('prototypes.paginationDefaultFromPartial'); // if only argument "required" is pagination // also default to empty methodArgs = t.assignmentPattern( methodArgs, t.objectExpression([ t.objectProperty( t.identifier('pagination'), paginationDefaultFromPartial ? t.callExpression( t.memberExpression(t.identifier("PageRequest"), t.identifier("fromPartial")), [t.objectExpression([])] ) : t.identifier('undefined'), false, false ) ]) ) } const body = t.blockStatement([ // const data = QueryAccountsRequest.encode(request).finish(); encodeData(requestType), // const promise = this.rpc.request("cosmos.auth.v1beta1.Query", "Accounts", data); promiseRequest(msg, packageImport), // return promise.then((data) => QueryAccountsResponse.decode(new _m0.Reader(data))); returnPromise(responseType, context) ]); if (context.pluginValue('classesUseArrowFunctions')) { return classProperty( t.identifier(name), arrowFunctionExpression( [methodArgs], body, returnReponseType(responseType), true ), undefined, undefined, undefined, undefined, undefined, undefined, makeComment(comment) as t.CommentLine[], ); } return classMethod( 'method', t.identifier(name), [ methodArgs, ...(context.options.interfaces.enabled && context.options.interfaces.useUseInterfacesParams ? [ t.assignmentPattern( identifier( 'useInterfaces', t.tsTypeAnnotation(t.tsBooleanKeyword()) ), t.identifier( (context.pluginValue('interfaces.useByDefaultRpc') ?? true).toString() ) ) ] : []), ], body, returnReponseType(responseType) ); }; const rpcTxClassMethod = ( context: GenericParseContext, name: string, msg: string, svc: ProtoServiceMethod, packageImport: string ) => { const requestType = svc.requestType; const responseType = t.tsTypeReference( t.identifier("DeliverTxResponse"), ); const comment = svc.comment ?? svc.name; const methodArgs: (t.Identifier | t.AssignmentPattern)[] = [ identifier("signerAddress", t.tsTypeAnnotation(t.tsStringKeyword())), identifier( "message", t.tsTypeAnnotation(t.tsTypeReference(t.identifier(requestType))) ), t.assignmentPattern( identifier( "fee", t.tsTypeAnnotation( t.tsUnionType([ t.tsNumberKeyword(), t.tsTypeReference(t.identifier("StdFee")), t.tsLiteralType(t.stringLiteral("auto")), ]) ) ), t.stringLiteral("auto") ), t.assignmentPattern( identifier("memo", t.tsTypeAnnotation(t.tsStringKeyword())), t.stringLiteral("") ), ]; const body = t.blockStatement([ // generate: // const data = [ // { // typeUrl: MsgCreateValidator.typeUrl, // value: message, // }, // ]; t.variableDeclaration('const', [ t.variableDeclarator( t.identifier('data'), t.arrayExpression([ t.objectExpression([ t.objectProperty(t.identifier('typeUrl'), t.memberExpression(t.identifier(svc.requestType), t.identifier('typeUrl'))), t.objectProperty(t.identifier('value'), t.identifier('message')) ]) ]) ) ]), // generate: // return this.rpc.signAndBroadcast!( // signerAddress, // data, // fee, // memo // ); t.returnStatement( t.callExpression( t.memberExpression( t.memberExpression( t.thisExpression(), t.identifier('rpc') ), t.identifier('signAndBroadcast!') ), [ t.identifier('signerAddress'), t.identifier('data'), t.identifier('fee'), t.identifier('memo') ] ) ) ]); if (context.pluginValue('classesUseArrowFunctions')) { return classProperty( t.identifier(name), arrowFunctionExpression( methodArgs, body, returnReponseType(responseType), true ), undefined, undefined, undefined, undefined, undefined, undefined, makeComment(comment) as t.CommentLine[], ); } return classMethod( 'method', t.identifier(name), methodArgs, body, returnReponseType(responseType) ); }; const rpcClassConstructor = ( context: GenericParseContext, methods: string[] ) => { const useTelescopeGeneratedType = context.pluginValue('prototypes.typingsFormat.useTelescopeGeneratedType'); let bound = []; if (!context.pluginValue('classesUseArrowFunctions')) { bound = methods.map(method => bindThis(method)); } return classMethod( 'constructor', t.identifier('constructor'), [ identifier( 'rpc', t.tsTypeAnnotation( t.tsTypeReference( t.identifier(useTelescopeGeneratedType ? 'TxRpc' : 'Rpc') ) ) ) ], t.blockStatement([ t.expressionStatement( t.assignmentExpression( '=', t.memberExpression( t.thisExpression(), t.identifier('rpc') ), t.identifier('rpc') ) ), /// methods ...bound ]) ); }; export const createRpcClientInterface = ( context: GenericParseContext, service: ProtoService, name?: string, methodKeys?: string[], nameMapping?: { [key: string]: string } ) => { const serviceImplement = context.pluginValue('rpcClients.serviceImplement'); const camelRpcMethods = context.pluginValue('rpcClients.camelCase'); const keys = methodKeys && methodKeys.length ? methodKeys : Object.keys(service.methods ?? {}); const methods = keys .map((key) => { const methodName = camelRpcMethods ? camel(key) : key; const implementType = getServiceImplement( service.name, context.ref.proto.package, methodName, serviceImplement ); const method = service.methods[key]; if(!method){ return null; } const nameWithPkg = `${context.ref.proto.package}.${methodName}`; const methodAlias = nameMapping && nameMapping[nameWithPkg] ? nameMapping[nameWithPkg] : methodName; const leadingComments = method.comment ? [commentBlock(processRpcComment(method))] : []; let trailingComments = []; switch (implementType) { case "Tx": context.addUtil("DeliverTxResponse"); context.addUtil("StdFee"); return rpcTxMethodDefinition( methodAlias, method, trailingComments, leadingComments ); case "Query": default: return rpcMethodDefinition( methodAlias, method, trailingComments, leadingComments ); } }).filter(Boolean); const obj = t.exportNamedDeclaration( t.tsInterfaceDeclaration( t.identifier(name ?? service.name), null, [], t.tsInterfaceBody( [ ...methods ] ) ) ); if (service.comment) { obj.leadingComments = [commentBlock(`* ${service.comment} `)]; } return obj; }; export const getRpcClassName = (service: ProtoService) => { return `${service.name}ClientImpl`; } export const createRpcClientClass = ( context: GenericParseContext, service: ProtoService ) => { const serviceImplement = context.pluginValue('rpcClients.serviceImplement'); const useTelescopeGeneratedType = context.pluginValue('prototypes.typingsFormat.useTelescopeGeneratedType'); if(useTelescopeGeneratedType){ context.addUtil('TxRpc'); } else { context.addUtil('Rpc'); } BinaryCoder.addUtil(context, 'reader'); const camelRpcMethods = context.pluginValue('rpcClients.camelCase'); const name = getRpcClassName(service); const implementsName = service.name; const methodNames = Object.keys(service.methods ?? {}) .map(key => { return camelRpcMethods ? camel(key) : key }); const methods = Object.keys(service.methods ?? {}) .map(key => { const name = camelRpcMethods ? camel(key) : key; const implementType = getServiceImplement( service.name, context.ref.proto.package, name, serviceImplement ); const method = service.methods[key]; switch (implementType) { case "Tx": context.addUtil("DeliverTxResponse"); context.addUtil("StdFee"); return rpcTxClassMethod( context, name, key, method, context.ref.proto.package + '.' + service.name ); case "Query": default: return rpcClassMethod( context, name, key, method, context.ref.proto.package + '.' + service.name ) } }); return t.exportNamedDeclaration( classDeclaration( t.identifier(name), null, t.classBody([ classProperty( t.identifier('rpc'), null, t.tsTypeAnnotation( t.tsTypeReference( t.identifier(useTelescopeGeneratedType ? 'TxRpc' : 'Rpc') ) ), null, false, false, true, 'private' ), // CONSTRUCTOR rpcClassConstructor(context, methodNames), // METHODS ...methods ]), null, [ t.tsExpressionWithTypeArguments( t.identifier(implementsName) ) ] ) ); }; export const createRpcInterface = (context: GenericParseContext, service: ProtoService) => { return t.tsInterfaceDeclaration( t.identifier('Rpc'), null, [], t.tsInterfaceBody( [ t.tsMethodSignature( t.identifier('request'), null, [ identifier('service', t.tsTypeAnnotation( t.tsStringKeyword() ) ), identifier('method', t.tsTypeAnnotation( t.tsStringKeyword() ) ), identifier('data', t.tsTypeAnnotation( t.tsTypeReference( t.identifier('Uint8Array') ) ) ) ], t.tsTypeAnnotation( t.tsTypeReference( t.identifier('Promise'), t.tsTypeParameterInstantiation( [ t.tsTypeReference( t.identifier('Uint8Array') ) ] ) ) ) ) ] ) ) }