Skip to content

Commit

Permalink
add java-maven test
Browse files Browse the repository at this point in the history
  • Loading branch information
Thomas Schuehly committed Aug 27, 2023
1 parent e5fa506 commit d93fba8
Show file tree
Hide file tree
Showing 16 changed files with 543 additions and 115 deletions.
23 changes: 19 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:
pull_request:
branches: [ main ]
jobs:
test:
setup-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
Expand All @@ -24,16 +24,31 @@ jobs:
./gradlew :core:publishToMavenLocal
./gradlew :thymeleaf:publishToMavenLocal
./gradlew :jte:publishToMavenLocal
- name: run thymeleaf tests
test-kotlin:
needs:
test
runs-on: ubuntu-latest
steps:
- name: run thymeleaf kotlin tests
run: |
cd ./examples/thymeleaf-kotlin-example
chmod +x ./gradlew
./gradlew test
publish:
test-java:
needs:
test
runs-on: ubuntu-latest
steps:
- name: run thymeleaf kotlin tests
run: |
cd ./examples/thymeleaf-java-example
chmod +x ./gradlew
./gradlew test
publish:
needs:
[ test-java,test-kotlin ]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Java
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,11 @@ import de.tschuehly.spring.viewcomponent.core.IViewContext

class ViewContextContainer(
vararg val viewContexts: IViewContext
)
){
companion object{
@JvmStatic
fun of(vararg viewContexts: IViewContext): ViewContextContainer {
return ViewContextContainer(*viewContexts)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class ViewComponentParser {
viewComponentName: String,
messager: Messager? = null
) {
val resourceHtmlFile = getResourceFile(srcFile, buildType)
val resourceHtmlFile = getResourceFile(srcFile, buildType, messager)
writeFile(
srcHtmlFile = srcFile,
resourceHtmlFile = resourceHtmlFile,
Expand All @@ -30,8 +30,8 @@ class ViewComponentParser {
)
}

private fun getResourceFile(srcFile: File, buildType: BuildType): File {
val (rootDir, packagePath) = getRootDirAndPackagePath(srcFile)
private fun getResourceFile(srcFile: File, buildType: BuildType, messager: Messager?): File {
val (rootDir, packagePath) = getRootDirAndPackagePath(srcFile, messager)
val resourceDir = if (buildType == BuildType.GRADLE) {
FileSystems.getDefault()
.getPath(rootDir, "build", "resources", "main", packagePath)
Expand All @@ -48,15 +48,16 @@ class ViewComponentParser {
return resourceHtmlFile
}

private fun getRootDirAndPackagePath(srcFile: File): Pair<String, String> {
private fun getRootDirAndPackagePath(srcFile: File, messager: Messager?): Pair<String, String> {
val seperator = FileSystems.getDefault().separator
val srcMain = "src${seperator}main${seperator}"
val list = if (srcFile.path.contains("${srcMain}kotlin")) {
srcFile.path.split("${srcMain}kotlin")
} else if (srcFile.path.contains("${srcMain}java")) {
srcFile.path.split(("${srcMain}java"))
} else {
throw ViewComponentProcessingException("No src main", null)
messager?.printMessage(Diagnostic.Kind.ERROR, "No src main found")
throw ViewComponentProcessingException("No src main found", null)
}
return list[0] to list[1].split(srcFile.name)[0]
}
Expand Down Expand Up @@ -84,9 +85,12 @@ class ViewComponentParser {
viewActionAttrVal
}
val method = methodList.find { it.name.lowercase() == methodName.lowercase() }
?: throw RuntimeException("viewAction method $methodName does not exist")
?: let {
messager?.printMessage(Diagnostic.Kind.ERROR, "viewAction method $methodName does not exist")
throw ViewComponentProcessingException("viewAction method $methodName does not exist", null)
}

val (htmxAttr, path) = getHtmxAttribute(viewComponentName, method)
val (htmxAttr, path) = getHtmxAttribute(viewComponentName, method, messager)
val htmxAttrVal = viewActionAttrVal.replace(methodName, path, true)
val newLine =
"$beforeViewAction$htmxAttr\"$htmxAttrVal\" hx-target=\"#$viewComponentName\"$afterViewActionAttrVal"
Expand All @@ -109,15 +113,22 @@ class ViewComponentParser {
resourceFileWriter.close()
}

private fun getHtmxAttribute(viewComponentName: String, method: ViewActionMethod): Pair<String, String> {
private fun getHtmxAttribute(
viewComponentName: String,
method: ViewActionMethod,
messager: Messager?
): Pair<String, String> {
val action = if (method.path == "") "/$viewComponentName/${method.name}" else method.path
return when (method.clazz) {
GetViewAction::class.java -> Pair("hx-get=", action.lowercase())
PostViewAction::class.java -> Pair("hx-post=", action.lowercase())
PutViewAction::class.java -> Pair("hx-put=", action.lowercase())
PatchViewAction::class.java -> Pair("hx-patch=", action.lowercase())
DeleteViewAction::class.java -> Pair("hx-delete=", action.lowercase())
else -> throw RuntimeException("No annotation found on $viewComponentName")
else -> let {
messager?.printMessage(Diagnostic.Kind.ERROR, "No annotation found on $viewComponentName")
throw ViewComponentProcessingException("No annotation found on $viewComponentName", null)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import java.io.IOException
import java.nio.file.FileSystems
import java.nio.file.Path
import java.nio.file.Paths
import java.util.*
import javax.annotation.processing.*
import javax.lang.model.SourceVersion
import javax.lang.model.element.Element
Expand Down Expand Up @@ -35,23 +36,23 @@ class ViewComponentProcessor : AbstractProcessor() {

val seperator = FileSystems.getDefault().separator
val packagePath = "${element.enclosingElement}".replace(".", seperator)
val srcDir = getSrcDir(rootDir)
val srcDir = getSrcDir(rootDir, proc.messager)
val viewComponentDir = FileSystems.getDefault().getPath(srcDir.toString(), packagePath)
proc.messager.printMessage(Diagnostic.Kind.NOTE, "SrcDirPath: " + viewComponentDir.absolutePathString())

val srcHtmlFile = getSrcHtmlFile(viewComponentDir, element.simpleName, proc.messager)
val methodList = getViewActionMethods(element)

val viewComponentName = element.simpleName.toString().lowercase()
verifyRenderMethodExists(element)
verifyRenderMethodExists(element, proc.messager)
viewComponentParser.parseFile(srcHtmlFile, buildType, methodList, viewComponentName, proc.messager)
}
}
return true
}


private fun getSrcDir(rootDir: String): Path {
private fun getSrcDir(rootDir: String, messager: Messager?): Path {
val mainPath = FileSystems.getDefault()
.getPath(rootDir, "src", "main")
if (mainPath.resolve("kotlin").exists()) {
Expand All @@ -60,18 +61,20 @@ class ViewComponentProcessor : AbstractProcessor() {
if (mainPath.resolve("java").exists()) {
return mainPath.resolve("java")
}
messager?.printMessage(Diagnostic.Kind.ERROR, "No src main found")
throw ViewComponentProcessingException("$mainPath/src/main/[java,kotlin] not found", null)
}

private fun ProcessingEnvironment.getJavaRootDir(): String {
try {
val sourceFile = this.filer.createClassFile("test.java")
val sourceFile = this.filer.createClassFile("gen_${Date().toInstant().toEpochMilli()}.java")
val srcDirPath = Paths.get(sourceFile.toUri()).toString()
return srcDirPath
} catch (e: IOException) {
processingEnv.messager.printMessage(Diagnostic.Kind.WARNING, "Unable to determine source file path!")
}
throw ViewComponentProcessingException("", null)
this.messager.printMessage(Diagnostic.Kind.ERROR, "Could not get create class file")
throw ViewComponentProcessingException("Could not get create class file", null)
}

private fun ProcessingEnvironment.getKotlinRootDir(): String? {
Expand All @@ -89,20 +92,31 @@ class ViewComponentProcessor : AbstractProcessor() {
if (filePath.contains("build")) {
return filePath.split("build")[0] to BuildType.GRADLE
}
this.messager.printMessage(Diagnostic.Kind.ERROR, "No build or target folder found")
throw ViewComponentProcessingException("No build or target folder found", null)

}

private fun verifyRenderMethodExists(element: Element) {
private fun verifyRenderMethodExists(element: Element, messager: Messager) {
val renderReturnType = element.enclosedElements.filter { it.kind == ElementKind.METHOD }
.find { it.simpleName.toString() == "render" }
?.asType()
?: throw ViewComponentProcessingException(
"You need to define a render method in the $element that returns a ViewContext",
null
)
?: let {
messager.printMessage(
Diagnostic.Kind.ERROR,
"You need to define a render method in the $element that returns a ViewContext"
)
throw ViewComponentProcessingException(
"You need to define a render method in the $element that returns a ViewContext",
null
)
}

if (!renderReturnType.toString().contains("ViewContext")) {
messager.printMessage(
Diagnostic.Kind.ERROR,
"Your render method in the $element needs to return a ViewContext"
)
throw ViewComponentProcessingException(
"Your render method in the $element needs to return a ViewContext",
null
Expand All @@ -120,8 +134,11 @@ class ViewComponentProcessor : AbstractProcessor() {
}
messager.printMessage(Diagnostic.Kind.NOTE, "Didn't find file at ${file.path}")
}

throw ViewComponentProcessingException("Couldn't find a file at ${srcDirPath}", null)
messager.printMessage(
Diagnostic.Kind.ERROR,
"Couldn't find a file at $srcDirPath"
)
throw ViewComponentProcessingException("Couldn't find a file at $srcDirPath", null)
}

private fun getViewActionMethods(element: Element): List<ViewActionMethod> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,41 @@
package de.tschuehly.spring.viewcomponent.thymeleaf.application.web;

import de.tschuehly.spring.viewcomponent.core.component.ViewContextContainer;
import de.tschuehly.spring.viewcomponent.thymeleaf.ViewContext;
import de.tschuehly.spring.viewcomponent.thymeleaf.application.web.action.ActionViewComponent;
import de.tschuehly.spring.viewcomponent.thymeleaf.application.web.index.IndexViewComponent;
import de.tschuehly.spring.viewcomponent.thymeleaf.application.web.layout.LayoutViewComponent;
import de.tschuehly.spring.viewcomponent.thymeleaf.application.web.nav.NavViewComponent;
import de.tschuehly.spring.viewcomponent.thymeleaf.application.web.simple.SimpleViewComponent;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;

@Controller
public class TestController {
private final SimpleViewComponent simpleViewComponent;
private final IndexViewComponent indexViewComponent;
private final ActionViewComponent actionViewComponent;
private final LayoutViewComponent layoutViewComponent;
private final NavViewComponent navViewComponent;


public TestController(
SimpleViewComponent simpleViewComponent,
IndexViewComponent indexViewComponent
IndexViewComponent indexViewComponent,
ActionViewComponent actionViewComponent,
LayoutViewComponent layoutViewComponent,
NavViewComponent navViewComponent
) {
this.simpleViewComponent = simpleViewComponent;
this.indexViewComponent = indexViewComponent;
this.actionViewComponent = actionViewComponent;
this.layoutViewComponent = layoutViewComponent;
this.navViewComponent = navViewComponent;
}

@GetMapping("/")
ViewContext indexComponent(){
ViewContext indexComponent() {
return indexViewComponent.render();
}

Expand All @@ -29,7 +44,35 @@ ViewContext simple() {
return simpleViewComponent.render();
}

@GetMapping("/template-test")
@GetMapping("/layout")
ViewContext layoutComponent() {
return layoutViewComponent.render(simpleViewComponent.render());
}

@GetMapping("/action")
ViewContext actionComponent() {
return actionViewComponent.render();
}

@GetMapping("/nested-action")
ViewContext nestedActionComponent() {
return layoutViewComponent.render(actionViewComponent.render());
}

@GetMapping("/multi")
ViewContextContainer multiComponent() {
return ViewContextContainer.of(
simpleViewComponent.render(),
layoutViewComponent.render(simpleViewComponent.render())
);
}

@ModelAttribute("navbar")
ViewContext headerComponent() {
return navViewComponent.render("This is a NavBar");
}

@GetMapping("/resource-template")
String templateTest() {
return "template-test";
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<script src="http://localhost:35729/livereload.js"></script>
<script defer src="/webjars/htmx.org/dist/htmx.min.js"></script>
</head>
<body>
<h2>ViewAction Get CountUp</h2>

<button view:action="countUp">Default ViewAction [GET]</button>
<h3 th:text="${counter}"></h3>

<h2>ViewAction Post AddItem</h2>
<form view:action="addItem">
<input type="text" name="item">
<button type="submit">Save Item</button>
</form>
<table>
<tr>
<th>Item</th>
<th>Action</th>
</tr>
<tr th:each="item : ${itemList}">
<td>
<span th:text="${item.value}"></span>
</td>
<td>
<button th:view:action="|deleteItem?id=${item.key}|">Delete Item [[${item.key}]]</button>
</td>
</tr>
</table>

<h2>ViewAction Put/Patch Person Form</h2>

<form th:object="${person}" style="display: inline-grid; gap: 0.5rem">
<label>
Name <input type="text" th:field="*{name}">
</label>
<label>
Age: <input type="number" th:field="*{age}">
</label>
<label>
Location: <input type="text" th:field="*{location}">
</label>
<button type="submit" view:action="savePersonPut">Save Changes using Put</button>
<button type="submit" view:action="savePersonPatch">Save Changes using Patch</button>
</form>
</body>
</html>
Loading

0 comments on commit d93fba8

Please sign in to comment.