Skip to content

Commit

Permalink
Implement REST service layer generation technique which unfolds all a…
Browse files Browse the repository at this point in the history
…rguments

Fixes vojtechhabarta#792

Signed-off-by: Oleksandr Porunov <alexandr.porunov@gmail.com>
  • Loading branch information
porunov committed Feb 13, 2022
1 parent 58b8c03 commit a59f163
Show file tree
Hide file tree
Showing 9 changed files with 207 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ public class Settings {
public boolean generateJaxrsApplicationClient = false;
public boolean generateSpringApplicationInterface = false;
public boolean generateSpringApplicationClient = false;
public boolean generateClientAsService = false;
public boolean skipNullValuesForOptionalServiceArguments = false;
public boolean scanSpringApplication;
public List<Class<? extends Annotation>> springCustomQueryParameterAnnotations = new ArrayList<>();
public List<Class<? extends Annotation>> springCustomRequestBodyAnnotations = new ArrayList<>();
Expand Down Expand Up @@ -432,6 +434,15 @@ public void validate() {
if (generateSpringApplicationClient && outputFileType != TypeScriptFileType.implementationFile) {
throw new RuntimeException("'generateSpringApplicationClient' can only be used when generating implementation file ('outputFileType' parameter is 'implementationFile').");
}

if(generateClientAsService && !(generateSpringApplicationClient || generateJaxrsApplicationClient)){
throw new RuntimeException("'generateClientAsService' can only be used when application client generation is enabled via 'generateSpringApplicationClient' or 'generateJaxrsApplicationClient'.");
}

if(skipNullValuesForOptionalServiceArguments && !generateClientAsService){
throw new RuntimeException("'skipNullValuesForOptionalServiceArguments' can only be used when application client as a service generation is enabled via 'generateClientAsService'.");
}

if (jaxrsNamespacing != null) {
TypeScriptGenerator.getLogger().warning("Parameter 'jaxrsNamespacing' is deprecated. Use 'restNamespacing' parameter.");
if (restNamespacing == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,16 @@
import cz.habarta.typescript.generator.emitter.TsAssignmentExpression;
import cz.habarta.typescript.generator.emitter.TsBeanCategory;
import cz.habarta.typescript.generator.emitter.TsBeanModel;
import cz.habarta.typescript.generator.emitter.TsBinaryExpression;
import cz.habarta.typescript.generator.emitter.TsBinaryOperator;
import cz.habarta.typescript.generator.emitter.TsCallExpression;
import cz.habarta.typescript.generator.emitter.TsConstructorModel;
import cz.habarta.typescript.generator.emitter.TsEnumModel;
import cz.habarta.typescript.generator.emitter.TsExpression;
import cz.habarta.typescript.generator.emitter.TsExpressionStatement;
import cz.habarta.typescript.generator.emitter.TsHelper;
import cz.habarta.typescript.generator.emitter.TsIdentifierReference;
import cz.habarta.typescript.generator.emitter.TsIfStatement;
import cz.habarta.typescript.generator.emitter.TsMemberExpression;
import cz.habarta.typescript.generator.emitter.TsMethodModel;
import cz.habarta.typescript.generator.emitter.TsModel;
Expand All @@ -42,6 +45,7 @@
import cz.habarta.typescript.generator.emitter.TsTaggedTemplateLiteral;
import cz.habarta.typescript.generator.emitter.TsTemplateLiteral;
import cz.habarta.typescript.generator.emitter.TsThisExpression;
import cz.habarta.typescript.generator.emitter.TsVariableDeclarationStatement;
import cz.habarta.typescript.generator.parser.BeanModel;
import cz.habarta.typescript.generator.parser.EnumModel;
import cz.habarta.typescript.generator.parser.MethodModel;
Expand Down Expand Up @@ -729,13 +733,15 @@ private TsMethodModel processRestMethod(TsModel tsModel, SymbolTable symbolTable
parameters.add(processParameter(symbolTable, method, method.getEntityParam()));
}
// query params
final TsParameterModel queryParameter = convertRestParams(method.getQueryParams(), symbolTable, method, tsModel, "queryParams", "QueryParams");
if(queryParameter != null){
final List<TsProperty> allQuerySingles = new ArrayList<>();
final TsParameterModel queryParameter = convertRestParams(method.getQueryParams(), parameters, symbolTable, method, tsModel, "queryParams", "QueryParams", allQuerySingles);
if(queryParameter != null && !settings.generateClientAsService){
parameters.add(queryParameter);
}
// body params
final TsParameterModel headersParameter = convertRestParams(method.getHeaders(), symbolTable, method, tsModel, "headers", "Headers");
if(headersParameter != null){
// header params
final List<TsProperty> allHeaderSingles = new ArrayList<>();
final TsParameterModel headersParameter = convertRestParams(method.getHeaders(), parameters, symbolTable, method, tsModel, "headers", "Headers", allHeaderSingles);
if(headersParameter != null && !settings.generateClientAsService){
parameters.add(headersParameter);
}
if (optionsType != null) {
Expand All @@ -758,6 +764,14 @@ private TsMethodModel processRestMethod(TsModel tsModel, SymbolTable symbolTable
final List<TsStatement> body;
if (implement) {
body = new ArrayList<>();
if(settings.generateClientAsService){
if(!allQuerySingles.isEmpty()){
initServiceParameters(body, allQuerySingles, "queryParams");
}
if(!allHeaderSingles.isEmpty()){
initServiceParameters(body, allHeaderSingles, "headers");
}
}
body.add(new TsReturnStatement(
new TsCallExpression(
new TsMemberExpression(new TsMemberExpression(new TsThisExpression(), "httpClient"), "request"),
Expand All @@ -779,7 +793,49 @@ private TsMethodModel processRestMethod(TsModel tsModel, SymbolTable symbolTable
return tsMethodModel;
}

private TsParameterModel convertRestParams(List<RestParam> restParams, SymbolTable symbolTable, RestMethodModel method, TsModel tsModel, String parameterName, String beanSuffix){
private void initServiceParameters(List<TsStatement> body, List<TsProperty> singleServiceParams, String argumentName){
body.add(new TsVariableDeclarationStatement(
false,
argumentName,
TsType.Any,
new TsObjectLiteral()
));
for(TsProperty property : singleServiceParams){

String validJavaScriptArgumentName = Utils.toValidJavaScriptVariableName(property.getName());

TsExpressionStatement assignmentExpressionStatement = new TsExpressionStatement(
new TsAssignmentExpression(
new TsMemberExpression(
new TsIdentifierReference(argumentName),
property.getName()),
new TsIdentifierReference(validJavaScriptArgumentName)
)
);

if(property.getTsType() instanceof TsType.OptionalType){

body.add(
new TsIfStatement(
new TsBinaryExpression(
new TsIdentifierReference(validJavaScriptArgumentName),
TsBinaryOperator.NEQ,
settings.skipNullValuesForOptionalServiceArguments ?
TsIdentifierReference.Null : TsIdentifierReference.Undefined
),
Collections.singletonList(assignmentExpressionStatement)
)
);

} else {

body.add(assignmentExpressionStatement);

}
}
}

private TsParameterModel convertRestParams(List<RestParam> restParams, List<TsParameterModel> parameters, SymbolTable symbolTable, RestMethodModel method, TsModel tsModel, String parameterName, String beanSuffix, List<TsProperty> allSingles){
if (restParams == null || restParams.isEmpty()){
return null;
}
Expand All @@ -798,7 +854,13 @@ private TsParameterModel convertRestParams(List<RestParam> restParams, SymbolTab
if (restParam instanceof RestParam.Single) {
final MethodParameterModel restParamMethodParameterModel = ((RestParam.Single) restParam).getRestParam();
final TsType type = typeFromJava(symbolTable, restParamMethodParameterModel.getType(), method.getName(), method.getOriginClass());
currentSingles.add(new TsProperty(restParamMethodParameterModel.getName(), restParam.required ? type : new TsType.OptionalType(type)));
TsProperty property = new TsProperty(restParamMethodParameterModel.getName(), restParam.required ? type : new TsType.OptionalType(type));
currentSingles.add(property);
allSingles.add(property);
if(settings.generateClientAsService) {
String validJavaScriptArgumentName = Utils.toValidJavaScriptVariableName(property.getName());
parameters.add(new TsParameterModel(validJavaScriptArgumentName, property.getTsType()));
}
}
if (restParam instanceof RestParam.Bean) {
final BeanModel paramBean = ((RestParam.Bean) restParam).getBean();
Expand All @@ -825,6 +887,7 @@ private TsParameterModel convertRestParams(List<RestParam> restParams, SymbolTab
}
flushSingles.run();
}

boolean allParamsOptional = restParams.stream().noneMatch(param -> param.required);
TsType.IntersectionType paramType = new TsType.IntersectionType(types);
return new TsParameterModel(parameterName, allParamsOptional ? new TsType.OptionalType(paramType) : paramType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@

public enum TsBinaryOperator implements Emittable {

BarBar("||");
BarBar("||"),
NEQ("!=");

private final String formatted;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
public class TsIdentifierReference extends TsExpression {

public static final TsIdentifierReference Undefined = new TsIdentifierReference("undefined");
public static final TsIdentifierReference Null = new TsIdentifierReference("null");

private final String identifier;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -508,4 +508,35 @@ public static <T> Supplier<T> memoize(Supplier<T> supplier) {
return () -> value.updateAndGet(current -> current != null ? current : Objects.requireNonNull(supplier.get()));
}

public static String toValidJavaScriptVariableName(String variableName){

StringBuilder validJavaScriptVariableName = new StringBuilder();

boolean convertNextLetterToCapital = false;

for(int i=0; i<variableName.length(); i++){

char currentChar = variableName.charAt(i);

if(currentChar >= '0' && currentChar <= '9'){

if(validJavaScriptVariableName.length() == 0){
// Number prefix is forbidden
continue;
}

} else if (currentChar >= 'a' && currentChar <= 'z'){
if(convertNextLetterToCapital){
currentChar = Character.toUpperCase(currentChar);
}
} else if(currentChar != '_' && (currentChar < 'A' || currentChar > 'Z')){
convertNextLetterToCapital = validJavaScriptVariableName.length() > 0;
continue;
}
validJavaScriptVariableName.append(currentChar);
convertNextLetterToCapital = false;
}

return validJavaScriptVariableName.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,10 @@ class NewClass{}
Assertions.assertFalse(Utils.isPrimitiveType(NewClass.class));
}

@Test
public void testShouldConvertStringToCorrectJavaScriptVariableName(){
Assertions.assertEquals("helloWorld", Utils.toValidJavaScriptVariableName("hello-world"));
Assertions.assertEquals("helloWorld", Utils.toValidJavaScriptVariableName("123hello world"));
Assertions.assertEquals("hello123World", Utils.toValidJavaScriptVariableName("hello123World"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ public class GenerateTask extends DefaultTask {
public boolean generateJaxrsApplicationClient;
public boolean generateSpringApplicationInterface;
public boolean generateSpringApplicationClient;
public boolean generateClientAsService;
public boolean skipNullValuesForOptionalServiceArguments;
public boolean scanSpringApplication;
@Deprecated public RestNamespacing jaxrsNamespacing;
@Deprecated public String jaxrsNamespacingAnnotation;
Expand Down Expand Up @@ -187,6 +189,8 @@ private Settings createSettings(URLClassLoader classLoader) {
settings.generateJaxrsApplicationClient = generateJaxrsApplicationClient;
settings.generateSpringApplicationInterface = generateSpringApplicationInterface;
settings.generateSpringApplicationClient = generateSpringApplicationClient;
settings.generateClientAsService = generateClientAsService;
settings.skipNullValuesForOptionalServiceArguments = skipNullValuesForOptionalServiceArguments;
settings.scanSpringApplication = scanSpringApplication;
settings.jaxrsNamespacing = jaxrsNamespacing;
settings.setJaxrsNamespacingAnnotation(classLoader, jaxrsNamespacingAnnotation);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,25 @@ public class GenerateMojo extends AbstractMojo {
@Parameter
private List<String> springCustomRequestBodyAnnotations;

/**
* If <code>true</code> it will generate client application methods as service methods, meaning that they will
* accept all arguments as unfolded arguments. Otherwise, REST query parameters will be wrapped into an object <code>queryParams</code>.
* This parameter can be used only when <code>generateSpringApplicationClient</code> or <code>generateJaxrsApplicationClient</code> are set to <code>true</code>.
* Notice, currently only simple (non-bean) parameters will be detected. Currently, beans won't work for this type of client generation.
* If you need for beans to work as well, please, set this option to <code>false</code>. This flow is intended to be fixed in the future releases.
*/
@Parameter
private boolean generateClientAsService;

/**
* If <code>true</code> it will not pass optional parameters to the <code>HttpClient</code> if those parameters
* are set to <code>null</code>. Otherwise, only <code>undefined</code> parameters will be skipped.
* Notice, mandatory parameters which are set to <code>null</code> will still be passed.
* This parameter can be used only when <code>generateClientAsService</code> is set to <code>true</code>.
*/
@Parameter
private boolean skipNullValuesForOptionalServiceArguments;

/**
* Deprecated, use {@link #restNamespacing}.
*/
Expand Down Expand Up @@ -974,6 +993,8 @@ private Settings createSettings(URLClassLoader classLoader) {
settings.generateSpringApplicationInterface = generateSpringApplicationInterface;
settings.generateSpringApplicationClient = generateSpringApplicationClient;
settings.scanSpringApplication = scanSpringApplication;
settings.generateClientAsService = generateClientAsService;
settings.skipNullValuesForOptionalServiceArguments = skipNullValuesForOptionalServiceArguments;
settings.jaxrsNamespacing = jaxrsNamespacing;
settings.setJaxrsNamespacingAnnotation(classLoader, jaxrsNamespacingAnnotation);
settings.restNamespacing = restNamespacing;
Expand Down
Loading

0 comments on commit a59f163

Please sign in to comment.