UNPKG

@cosmology/ast

Version:
364 lines (327 loc) 10.3 kB
import { GenericParseContext } from "../../../../encoding"; import { ProtoService, ProtoServiceMethod } from "@cosmology/types"; import { arrowFunctionExpression, classDeclaration, classMethod, classProperty, commentBlock, identifier, tsMethodSignature } from '../../../../utils'; import { camel } from '@cosmology/utils'; import { processRpcComment, returnReponseType } from '../utils/rpc'; import { metadata, bindThis, makeComment, getRpcClassName } from './utils' import * as t from '@babel/types' function capitalizeFirstLetter(s) { return s.charAt(0).toUpperCase() + s.slice(1); } const gRPCWebMethodDefinition = ( context: GenericParseContext, name: string, msg: string, svc: ProtoServiceMethod, packageImport: string, trailingComments?: t.CommentBlock[], leadingComments?: t.CommentBlock[] ) => { const requestType = svc.requestType; const responseType = svc.responseType; //interface body is empty so nothing here const body = t.blockStatement([]) let optional = false; const fieldNames = Object.keys(svc.fields ?? {}) const hasParams = fieldNames.length > 0; if (!hasParams) { optional = true; } else if (hasParams && fieldNames.length === 1 && fieldNames.includes('pagination')) { // if only argument "required" is pagination // also default to empty optional = true; } const methodArgs: t.Identifier = identifier( 'request', t.tsTypeAnnotation( t.tsTypeReference( t.identifier('DeepPartial'), t.tsTypeParameterInstantiation( [ t.tsTypeReference( t.identifier(requestType) ) ] ) ) ), optional ); const metadataArgs: t.Identifier = metadata return tsMethodSignature( t.identifier(name), null, [ methodArgs, metadataArgs ], returnReponseType(responseType), trailingComments, leadingComments ) } export const createGrpcWebQueryInterface = ( context: GenericParseContext, service: ProtoService ) => { const camelRpcMethods = context.pluginValue('rpcClients.camelCase'); const keys = Object.keys(service.methods ?? {}); const methods = keys .map((key) => { const method = service.methods[key]; const name = camelRpcMethods ? camel(key) : key; const leadingComments = method.comment ? [commentBlock(processRpcComment(method))] : []; let trailingComments = []; return gRPCWebMethodDefinition( context, name, key, method, context.ref.proto.package + '.' + service.name, trailingComments, leadingComments ) }) const obj = t.exportNamedDeclaration( t.tsInterfaceDeclaration( t.identifier(service.name), null, [], t.tsInterfaceBody( [ ...methods, ] ), ), ) if (service.comment) { obj.leadingComments = [commentBlock(`* ${service.comment} `)]; } return obj; } const rpcClassConstructor = ( context: GenericParseContext, methods: string[] ) => { 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('Rpc') ) ) ) ], t.blockStatement([ t.expressionStatement( t.assignmentExpression( '=', t.memberExpression( t.thisExpression(), t.identifier('rpc') ), t.identifier('rpc') ) ), /// methods ...bound ]) ); }; const GrpcWebClassMethod = ( context: GenericParseContext, name: string, svc: ProtoServiceMethod, implementsName: string ) => { let partialName = 'DeepPartial'; let optional = false; 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(partialName), t.tsTypeParameterInstantiation( [ t.tsTypeReference( t.identifier(requestType) ) ] ) ) ), optional ); const metadataArgs: t.Identifier = metadata 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 ) ]) ) } // method from service do not have prefix let serviceName: string; if (implementsName === 'Service') { serviceName = '' } else { serviceName = implementsName } /* return this.rpc.unary( QueryParamsDesc, QueryParamsRequest.fromPartial(request), metadata, ); */ const body = t.blockStatement([ t.returnStatement( t.callExpression( t.memberExpression( t.memberExpression( t.thisExpression(), t.identifier('rpc') ), t.identifier('unary') ), [ //No Desc field so we need to modify it t.identifier(serviceName + capitalizeFirstLetter(name) + 'Desc'), t.callExpression( t.memberExpression( t.identifier(requestType), t.identifier('fromPartial') ), [ t.identifier('request') ] ), t.identifier('metadata') ] ) ) ]); 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, metadataArgs ], body, returnReponseType(responseType) ); }; export const createGrpcWebQueryClass = ( context: GenericParseContext, service: ProtoService ) => { //adding import context.addUtil('_m0'); context.addUtil('grpc'); context.addUtil('UnaryMethodDefinitionish'); // for other descriptor //use type DeepPartial context.addUtil('DeepPartial') let partialName = 'DeepPartial'; 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 method = service.methods[key]; const name = camelRpcMethods ? camel(key) : key; return GrpcWebClassMethod( context, name, method, implementsName ) }); return t.exportNamedDeclaration( classDeclaration( t.identifier(name), null, t.classBody([ classProperty( t.identifier('rpc'), null, t.tsTypeAnnotation( t.tsTypeReference( t.identifier('Rpc') ) ), null, false, false, true, 'private' ), // CONSTRUCTOR rpcClassConstructor(context, methodNames), // METHODS ...methods ]), null, [ t.tsExpressionWithTypeArguments( t.identifier(implementsName) ) ] ) ); };