UNPKG

@cosmology/ast

Version:
934 lines (889 loc) 51.7 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, optionalBool } from '../utils/rpc'; import { metadata, bindThis, makeComment, getRpcClassName } from './utils' import * as t from '@babel/types' const gRPCWebMethodDefinition = ( 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('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 createGrpcWebMsgInterface = ( 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( name, method, 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 rpcClassMethod = ( context: GenericParseContext, name: string, msg: string, svc: ProtoServiceMethod, packageImport: 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) ) ] ) ) ) ); 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 ) ]) ) } 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(requestType.concat('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 createGrpcWebMsgClass = ( context: GenericParseContext, service: ProtoService ) => { context.addUtil('UnaryMethodDefinitionish'); context.addUtil('_m0'); context.addUtil('DeepPartial'); context.addUtil('grpc'); 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 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('Rpc') ) ), null, false, false, true, 'private' ), // CONSTRUCTOR rpcClassConstructor(context, methodNames), // METHODS ...methods ]), null, [ t.tsExpressionWithTypeArguments( t.identifier(implementsName) ) ] ) ); }; const rpcMethodDef = ( svc: ProtoServiceMethod, ) => { const requestType = svc.requestType; const responseType = svc.responseType; const methodName = svc.name; return { methodName, requestType, responseType } } //return array of method Desc export const getMethodDesc = (context: GenericParseContext, service: ProtoService) => { //get all method that need Desc const methods = Object.keys(service.methods ?? {}) .map(key => { return rpcMethodDef( service.methods[key] ) }); const methodsDesc = [] //check if service name is 'service' if it is then ommit it because proto method doesn't contain service prefix in it methods let service_name = service.name if (service_name == 'Service') { service_name = '' } for (let i = 0; i < methods.length; i++) { const methodName = methods[i].methodName const requestType = methods[i].requestType const responseType = methods[i].responseType const methodDesc = t.exportNamedDeclaration( t.variableDeclaration( 'const', [ t.variableDeclarator( t.identifier(service_name + methodName + 'Desc: UnaryMethodDefinitionish'), t.objectExpression( [ t.objectProperty( t.identifier('methodName'), t.stringLiteral(methodName) ), t.objectProperty( t.identifier('service'), t.identifier(service.name + 'Desc') ), t.objectProperty( t.identifier('requestStream'), t.booleanLiteral(false) ), t.objectProperty( t.identifier('responseStream'), t.booleanLiteral(false) ), t.objectProperty( t.identifier('requestType'), t.tsAsExpression( t.objectExpression( [ t.objectMethod( 'method', t.identifier('serializeBinary'), [], t.blockStatement( [ t.returnStatement( t.callExpression( t.memberExpression( t.callExpression( t.memberExpression( t.identifier(requestType), t.identifier('encode') ), [ t.thisExpression() ] ), t.identifier('finish') ), [] ) ) ] ) ) ] ), t.tsAnyKeyword() ) ), t.objectProperty( t.identifier('responseType'), t.tsAsExpression( t.objectExpression( [ t.objectMethod( 'method', t.identifier('deserializeBinary'), [ identifier('data', t.tsTypeAnnotation( t.tsTypeReference( t.identifier('Uint8Array') ) )) ], t.blockStatement( [ t.returnStatement( t.objectExpression( [ t.spreadElement( t.callExpression( t.memberExpression( t.identifier(responseType), t.identifier('decode') ), [t.identifier('data')] ) ), t.objectMethod( 'method', t.identifier('toObject'), [], t.blockStatement( [ t.returnStatement( t.thisExpression() ) ] ) ) ] ) ) ] ) ) ] ), t.tsAnyKeyword() ) ), ] ) ) ] ) ) methodsDesc.push(methodDesc) } return methodsDesc } export const GetDesc = (context: GenericParseContext, service: ProtoService) => { const descName = service.name + 'Desc' const serviceName = context.ref.proto.package + '.' + service.name return t.exportNamedDeclaration( t.variableDeclaration( 'const', [ t.variableDeclarator( t.identifier(descName), t.objectExpression( [ t.objectProperty( t.identifier('serviceName'), t.stringLiteral(serviceName) ) ] ) ) ] ) ) } export const grpcWebRpcInterface = () => { return t.exportNamedDeclaration( t.tsInterfaceDeclaration( t.identifier('Rpc'), null, [], t.tsInterfaceBody( [ t.tsMethodSignature( t.identifier('unary'), t.tsTypeParameterDeclaration( [ t.tsTypeParameter( t.tsTypeReference( t.identifier('UnaryMethodDefinitionish') ), null, 'T' ) ] ), [ identifier('methodDesc', t.tsTypeAnnotation( t.tsTypeReference( t.identifier('T') ) )), identifier('request', t.tsTypeAnnotation( t.tsAnyKeyword() )), identifier('metadata', t.tsTypeAnnotation( t.tSUnionType( [ t.tsTypeReference( t.tsQualifiedName( t.identifier('grpc'), t.identifier('Metadata') ) ), t.tsUndefinedKeyword() ] ) )), ], t.tsTypeAnnotation( t.tSTypeReference( t.identifier('Promise'), t.tsTypeParameterInstantiation( [ t.tsAnyKeyword() ] ) ) ) ) ] ) )) } //you might not want to look at this export const getGrpcWebImpl = (context: GenericParseContext) => { context.addUtil('BrowserHeaders'); return t.exportNamedDeclaration( t.classDeclaration( t.identifier('GrpcWebImpl'), null, t.classBody( [ t.classProperty( t.identifier('host'), null, t.tsTypeAnnotation( t.tsStringKeyword() ) ), t.classProperty( t.identifier('options'), null, t.tsTypeAnnotation( t.tsTypeLiteral( [ t.tsPropertySignature( t.identifier('transport?'), t.tsTypeAnnotation( t.tsTypeReference( t.tsQualifiedName( t.identifier('grpc'), t.identifier('TransportFactory') ) ) ) ), t.tsPropertySignature( t.identifier('debug?'), t.tsTypeAnnotation( t.tsBooleanKeyword() ) ), t.tsPropertySignature( t.identifier('metadata?'), t.tsTypeAnnotation( t.tsTypeReference( t.tsQualifiedName( t.identifier('grpc'), t.identifier('Metadata') ) ) ) ) ] ) ) ), t.classMethod( "constructor", t.identifier('constructor'), [ identifier("host", t.tsTypeAnnotation( t.tsStringKeyword() )), identifier("options", t.tsTypeAnnotation( t.tsTypeLiteral( [ t.tsPropertySignature( t.identifier('transport?'), t.tsTypeAnnotation( t.tsTypeReference( t.tsQualifiedName( t.identifier('grpc'), t.identifier('TransportFactory') ) ) ) ), t.tsPropertySignature( t.identifier('debug?'), t.tsTypeAnnotation( t.tsBooleanKeyword() ) ), t.tsPropertySignature( t.identifier('metadata?'), t.tsTypeAnnotation( t.tsTypeReference( t.tsQualifiedName( t.identifier('grpc'), t.identifier('Metadata') ) ) ) ) ] ) )) ], t.blockStatement( [ t.expressionStatement( t.assignmentExpression( "=", t.memberExpression( t.thisExpression(), t.identifier('host') ), t.identifier('host') ) ), t.expressionStatement( t.assignmentExpression( "=", t.memberExpression( t.thisExpression(), t.identifier('options') ), t.identifier('options') ) ), ] ), ), t.classMethod( "method", t.identifier('unary<T extends UnaryMethodDefinitionish>'), [ identifier('methodDesc', t.tsTypeAnnotation(t.tsTypeReference(t.identifier('T')))), identifier('_request', t.tsTypeAnnotation(t.tsAnyKeyword())), identifier('metadata', t.tsTypeAnnotation(t.tsUnionType([t.tsTypeReference(t.tsQualifiedName(t.identifier('grpc'), t.identifier('Metadata'))), t.tsUndefinedKeyword()]))) ], t.blockStatement( [ t.variableDeclaration( "const", [ t.variableDeclarator( t.identifier('request'), t.objectExpression([ t.spreadElement(t.identifier('_request')), t.spreadElement(t.memberExpression(t.identifier('methodDesc'), t.identifier('requestType'))) ]) ) ] ), t.variableDeclaration( "const", [ t.variableDeclarator( t.identifier('maybeCombinedMetadata'), t.conditionalExpression( t.logicalExpression( "&&", t.identifier('metadata'), t.memberExpression( t.memberExpression( t.thisExpression(), t.identifier('options') ), t.identifier('metadata') ) ), t.newExpression( t.identifier('BrowserHeaders'), [ t.objectExpression( [ t.spreadElement( t.optionalMemberExpression( t.optionalMemberExpression( t.memberExpression( t.thisExpression(), t.identifier('options'), false ), t.identifier('metadata'), false, true ), t.identifier('headersMap'), false, false ) ), t.spreadElement( t.optionalMemberExpression( t.identifier('metadata'), t.identifier('headersMap'), false, true ) ) ] ) ] ), t.logicalExpression( "||", t.identifier('metadata'), t.memberExpression( t.memberExpression( t.thisExpression(), t.identifier('options') ), t.identifier('metadata') ) ) ) ) ] ), t.returnStatement( t.newExpression( t.identifier('Promise'), [ t.arrowFunctionExpression( [ t.identifier('resolve'), t.identifier('reject') ], t.blockStatement( [ t.expressionStatement( t.callExpression( t.memberExpression( t.identifier('grpc'), t.identifier('unary') ), [ t.identifier('methodDesc'), t.objectExpression( [ t.objectProperty( t.identifier('request'), t.identifier('request'), false, true ), t.objectProperty( t.identifier('host'), t.memberExpression( t.thisExpression(), t.identifier('host') ) ), t.objectProperty( t.identifier('metadata'), t.identifier('maybeCombinedMetadata') ), t.objectProperty( t.identifier('transport'), t.memberExpression( t.memberExpression( t.thisExpression(), t.identifier('options') ), t.identifier('transport') ) ), t.objectProperty( t.identifier('debug'), t.memberExpression( t.memberExpression( t.thisExpression(), t.identifier('options') ), t.identifier('debug') ) ), t.objectProperty( t.identifier('onEnd'), t.functionExpression( null, [ t.identifier('response') ], t.blockStatement( [ t.ifStatement( t.binaryExpression( "===", t.memberExpression( t.identifier('response'), t.identifier('status') ), t.memberExpression( t.memberExpression( t.identifier('grpc'), t.identifier('Code') ), t.identifier('OK'), ) ), t.blockStatement( [ t.expressionStatement( t.callExpression( t.identifier('resolve'), [ t.memberExpression( t.identifier('response'), t.identifier('message') ) ] ) ) ] ), t.blockStatement( [ t.variableDeclaration( "const", [ t.variableDeclarator( t.identifier('err'), t.tsAsExpression( t.newExpression( t.identifier('Error'), [ t.memberExpression( t.identifier('response'), t.identifier('statusMessage') ) ] ), t.tsAnyKeyword() ) ) ] ), t.expressionStatement( t.assignmentExpression( "=", t.memberExpression( t.identifier('err'), t.identifier('code') ), t.memberExpression( t.identifier('response'), t.identifier('status') ) ) ), t.expressionStatement( t.assignmentExpression( "=", t.memberExpression( t.identifier('err'), t.identifier('metadata') ), t.memberExpression( t.identifier('response'), t.identifier('trailers') ) ) ), t.expressionStatement( t.callExpression( t.identifier('reject'), [