feat: remove 5-tag limit and return all tags alphabetically (#7)

* feat: remove 5-tag limit and return all tags alphabetically

The /rest/home/tasks/tags endpoint previously returned only the top 5
most-used tags. This change removes that restriction so all existing
tags are returned, sorted alphabetically.

Fixes #6

Co-authored-by: Ricardo Campos <RMCampos@users.noreply.github.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* chore: fix test issue and add log version to the right place

* ci: add server ci

---------

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Ricardo Campos <RMCampos@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-10 13:00:10 -03:00
committed by GitHub
co-authored by claude[bot] <41898282+claude[bot]@users.noreply.github.com> Ricardo Campos Claude Sonnet 4.5
parent 2dc59d14d2
commit 3b335d6f1e
8 changed files with 89 additions and 55 deletions
+43
View File
@@ -0,0 +1,43 @@
name: Server API CI
on:
workflow_dispatch:
# run for all pushes, not only main
push:
branches:
- '**'
paths:
- 'server/**/*.java'
- 'server/**/*.xml'
- 'server/pom.xml'
jobs:
run-checks:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Set up Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '21'
cache: 'maven'
- name: Run Check Style
working-directory: ./server
run: ./mvnw --no-transfer-progress checkstyle:check -Dcheckstyle.skip=false
- name: Run build
working-directory: ./server
run: ./mvnw --no-transfer-progress clean compile -DskipTests
- name: Run tests
working-directory: ./server
run: ./mvnw --no-transfer-progress clean verify -P tests --file pom.xml
@@ -1,23 +1,11 @@
package br.com.tasknoteapp.server;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import br.com.tasknoteapp.server.service.AppVersionService;
/** Entrypoint of the Java API service application. */
@SpringBootApplication
public class JavaApiApplication implements ApplicationRunner {
private static final Logger logger = LoggerFactory.getLogger(JavaApiApplication.class);
@Autowired
private AppVersionService appVersionService;
public class JavaApiApplication {
/**
* Main method of the application.
@@ -27,9 +15,4 @@ public class JavaApiApplication implements ApplicationRunner {
public static void main(String[] args) {
SpringApplication.run(JavaApiApplication.class, args);
}
@Override
public void run(ApplicationArguments args) throws Exception {
logger.info("Task Note API started successfully - Version: {}", appVersionService.getVersion());
}
}
@@ -1,5 +1,6 @@
package br.com.tasknoteapp.server.controller;
import br.com.tasknoteapp.server.service.AppVersionService;
import java.sql.Connection;
import java.util.HashMap;
import java.util.Map;
@@ -10,8 +11,6 @@ import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import br.com.tasknoteapp.server.service.AppVersionService;
/** Controller to handle health check requests. */
@RestController
public class HealthController {
@@ -18,9 +18,9 @@ public class HomeController {
}
/**
* Get the top 5 tags.
* Get all existing tags, ordered alphabetically.
*
* @returns List of String with the tags.
* @return List of String with the tags.
*/
@GetMapping("/tasks/tags")
public List<String> getTasksTags() {
@@ -1,12 +1,20 @@
package br.com.tasknoteapp.server.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.boot.info.BuildProperties;
import org.springframework.context.event.EventListener;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
/** Service to retrieve application version information. */
@Service
public class AppVersionService {
private static final Logger logger = LoggerFactory.getLogger(AppVersionService.class);
private final BuildProperties buildProperties;
/**
@@ -14,16 +22,25 @@ public class AppVersionService {
*
* @param buildProperties the build properties injected by Spring Boot
*/
public AppVersionService(BuildProperties buildProperties) {
public AppVersionService(@Autowired(required = false) @Nullable BuildProperties buildProperties) {
this.buildProperties = buildProperties;
}
/** Logs the application version once the application context is fully started. */
@EventListener(ApplicationReadyEvent.class)
public void onApplicationReady() {
logger.info("Task Note API started successfully - Version: {}", getVersion());
}
/**
* Retrieves the application version combined with the build time.
*
* @return a string representing the application version and build time
*/
public String getVersion() {
if (buildProperties == null) {
return "unknown";
}
return buildProperties.getVersion() + "-" + buildProperties.getTime();
}
}
@@ -1,11 +1,9 @@
package br.com.tasknoteapp.server.service;
import br.com.tasknoteapp.server.response.TaskResponse;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@@ -25,37 +23,25 @@ public class HomeService {
}
/**
* Get up to 5 most used tags.
* Get all existing tags, ordered alphabetically.
*
* @return List of String with the tags.
*/
public List<String> getTopTasksTag() {
logger.info("Getting top tags for the tasks");
logger.info("Getting all tags for the tasks");
List<TaskResponse> tasks = taskService.getTasksByFilter("all");
logger.info(String.format(N_TASKS_FOUND, tasks.size()));
Map<String, Integer> tagsCount = new HashMap<>();
Set<String> tags = new HashSet<>();
for (TaskResponse task : tasks) {
if (tagsCount.size() == 5) {
break;
}
String tag = task.tag();
if (tag.isBlank()) {
tag = "untagged";
}
tagsCount.putIfAbsent(tag, 0);
tagsCount.put(tag, tagsCount.get(tag) + 1);
tags.add(tag);
}
Map<String, Integer> sortedDesc =
tagsCount.entrySet().stream()
.sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
.collect(
Collectors.toMap(
Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
return sortedDesc.keySet().stream().toList();
return tags.stream().sorted().toList();
}
}
@@ -32,8 +32,8 @@ class HomeServiceTest {
}
@Test
@DisplayName("Get top tasks tag should return up to 5 most used tags")
void getTopTasksTag_shouldReturnTopTags() {
@DisplayName("Get tasks tags should return all tags ordered alphabetically")
void getTopTasksTag_shouldReturnAllTagsAlphabetically() {
TaskResponse task1 =
new TaskResponse(1L, "Task 1", false, false, null, null, null, "tag1", List.of());
TaskResponse task2 =
@@ -54,15 +54,11 @@ class HomeServiceTest {
when(taskService.getTasksByFilter("all"))
.thenReturn(List.of(task1, task2, task3, task4, task5, task6, task7, task8));
List<String> topTags = homeService.getTopTasksTag();
List<String> tags = homeService.getTopTasksTag();
Assertions.assertNotNull(topTags);
Assertions.assertEquals(5, topTags.size());
Assertions.assertTrue(topTags.contains("tag1"));
Assertions.assertTrue(topTags.contains("tag2"));
Assertions.assertTrue(topTags.contains("tag3"));
Assertions.assertTrue(topTags.contains("tag4"));
Assertions.assertTrue(topTags.contains("tag5"));
Assertions.assertNotNull(tags);
Assertions.assertEquals(6, tags.size());
Assertions.assertEquals(List.of("tag1", "tag2", "tag3", "tag4", "tag5", "tag6"), tags);
}
@Test
+11 -1
View File
@@ -170,4 +170,14 @@ docker run --rm -p 8080:8080 \
-e CORS_ALLOWED_ORIGINS=http://localhost:5000 \
-e SERVER_SERVLET_CONTEXT_PATH=/ \
--network host \
docker.io/rmcampos/tasknote:api-latest
docker.io/rmcampos/tasknote:api-latest
## Running a single class test file
```
# For a single class
mvn test -P test -Dtest=YourTestClassName
# For a single class method
mvn test -P test -Dtest=YourTestClassName#method
```