@cosmology/ast
Version:
Cosmos TypeScript AST generation
155 lines (154 loc) • 7.09 kB
JavaScript
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, name, msg, svc, packageImport, trailingComments, leadingComments) => {
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 = identifier('request', t.tsTypeAnnotation(t.tsTypeReference(t.identifier('DeepPartial'), t.tsTypeParameterInstantiation([
t.tsTypeReference(t.identifier(requestType))
]))), optional);
const metadataArgs = metadata;
return tsMethodSignature(t.identifier(name), null, [
methodArgs,
metadataArgs
], returnReponseType(responseType), trailingComments, leadingComments);
};
export const createGrpcWebQueryInterface = (context, service) => {
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, methods) => {
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, name, svc, implementsName) => {
let partialName = 'DeepPartial';
let optional = false;
const requestType = svc.requestType;
const responseType = svc.responseType;
const comment = svc.comment ?? svc.name;
let methodArgs = identifier('request', t.tsTypeAnnotation(t.tsTypeReference(t.identifier(partialName), t.tsTypeParameterInstantiation([
t.tsTypeReference(t.identifier(requestType))
]))), optional);
const metadataArgs = 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;
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));
}
return classMethod('method', t.identifier(name), [
methodArgs,
metadataArgs
], body, returnReponseType(responseType));
};
export const createGrpcWebQueryClass = (context, service) => {
//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))
]));
};