Skip to content

Commit

Permalink
Support grouping guides into categories
Browse files Browse the repository at this point in the history
Closes gh-1
  • Loading branch information
mbhave committed May 24, 2024
1 parent d40f7ef commit cd3f130
Show file tree
Hide file tree
Showing 9 changed files with 259 additions and 46 deletions.
29 changes: 29 additions & 0 deletions src/main/java/io/spring/renderer/RendererProperties.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@
package io.spring.renderer;

import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import io.spring.renderer.RendererProperties.Webhook.Category;
import jakarta.validation.constraints.Pattern;

import org.springframework.boot.context.properties.ConfigurationProperties;
Expand All @@ -38,6 +41,8 @@ public class RendererProperties {
*/
private final Map<String, String> academy = new HashMap<>();

private final Map<String, Category> category = new HashMap<>();

public Github getGithub() {
return this.github;
}
Expand All @@ -46,6 +51,10 @@ public Map<String, String> getAcademy() {
return this.academy;
}

public Map<String, Category> getCategory() {
return this.category;
}

public static class Github {

/**
Expand Down Expand Up @@ -138,6 +147,26 @@ public void setDispatchToken(String dispatchToken) {
this.dispatchToken = dispatchToken;
}

public static class Category {

private String displayName;

private final Set<String> guide = new LinkedHashSet<>();

public String getDisplayName() {
return this.displayName;
}

public void setDisplayName(String displayName) {
this.displayName = displayName;
}

public Set<String> getGuide() {
return this.guide;
}

}

}

}
11 changes: 10 additions & 1 deletion src/main/java/io/spring/renderer/guides/GuideMetadata.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package io.spring.renderer.guides;

import java.util.Set;

import io.spring.renderer.github.Repository;

/**
Expand All @@ -29,9 +31,12 @@ class GuideMetadata {

private final String academyUrl;

GuideMetadata(Repository repository, String academyUrl) {
private final Set<String> category;

GuideMetadata(Repository repository, String academyUrl, Set<String> category) {
this.repository = repository;
this.academyUrl = academyUrl;
this.category = category;
}

public Repository getRepository() {
Expand All @@ -42,4 +47,8 @@ public String getAcademyUrl() {
return this.academyUrl;
}

public Set<String> getCategory() {
return this.category;
}

}
7 changes: 7 additions & 0 deletions src/main/java/io/spring/renderer/guides/GuideModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ class GuideModel extends RepresentationModel<GuideModel> {

private String academyUrl;

private String[] category;

GuideModel(GuideMetadata guideMetadata) {
Repository repository = guideMetadata.getRepository();
this.type = GuideType.fromRepositoryName(repository.getName());
Expand All @@ -76,6 +78,7 @@ class GuideModel extends RepresentationModel<GuideModel> {
this.projects = new String[0];
}
this.academyUrl = guideMetadata.getAcademyUrl();
this.category = guideMetadata.getCategory().toArray(new String[0]);
}

public String getName() {
Expand Down Expand Up @@ -122,4 +125,8 @@ public String getAcademyUrl() {
return this.academyUrl;
}

public String[] getCategory() {
return this.category;
}

}
21 changes: 19 additions & 2 deletions src/main/java/io/spring/renderer/guides/GuidesController.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,22 @@

package io.spring.renderer.guides;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import io.spring.renderer.RendererProperties;
import io.spring.renderer.RendererProperties.Webhook.Category;
import io.spring.renderer.github.GithubClient;
import io.spring.renderer.github.GithubResourceNotFoundException;
import io.spring.renderer.github.Repository;

import org.springframework.hateoas.CollectionModel;
import org.springframework.hateoas.MediaTypes;
import org.springframework.http.ResponseEntity;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
Expand All @@ -51,6 +56,8 @@ public class GuidesController {

private final GuideModelAssembler guideAssembler = new GuideModelAssembler();

private Set<String> DEFAULT_CATEGORY = Collections.singleton("Misc");

public GuidesController(GuideRenderer guideRenderer, GithubClient github, RendererProperties properties) {
this.guideRenderer = guideRenderer;
this.githubClient = github;
Expand All @@ -67,7 +74,7 @@ public CollectionModel<GuideModel> listGuides() {
List<Repository> repositories = this.githubClient
.fetchOrgRepositories(this.properties.getGithub().getOrganization());
List<GuideMetadata> guideMetadataList = repositories.stream()
.map((repository) -> new GuideMetadata(repository, getAcademyUrl(repository)))
.map((repository) -> new GuideMetadata(repository, getAcademyUrl(repository), getCategory(repository)))
.toList();
List<GuideModel> guideModels = this.guideAssembler.toCollectionModel(guideMetadataList)
.getContent()
Expand All @@ -84,6 +91,16 @@ public CollectionModel<GuideModel> listGuides() {
return resources;
}

private Set<String> getCategory(Repository repository) {
Map<String, Category> category = this.properties.getCategory();
Set<String> values = category.values()
.stream()
.filter((v) -> v.getGuide().contains(repository.getName()))
.map(Category::getDisplayName)
.collect(Collectors.toSet());
return (!CollectionUtils.isEmpty(values)) ? values : DEFAULT_CATEGORY;
}

private String getAcademyUrl(Repository repository) {
return this.properties.getAcademy().get(repository.getName());
}
Expand All @@ -97,7 +114,7 @@ public ResponseEntity<GuideModel> showGuide(@PathVariable String type, @PathVari
Repository repository = this.githubClient.fetchOrgRepository(this.properties.getGithub().getOrganization(),
guideType.getPrefix() + guide);
String academyUrl = this.properties.getAcademy().get(repository.getName());
GuideMetadata guideMetadata = new GuideMetadata(repository, academyUrl);
GuideMetadata guideMetadata = new GuideMetadata(repository, academyUrl, getCategory(repository));
GuideModel guideModel = this.guideAssembler.toModel(guideMetadata);
if (guideModel.getType().equals(GuideType.UNKNOWN)) {
return ResponseEntity.notFound().build();
Expand Down
5 changes: 4 additions & 1 deletion src/main/resources/application.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
spring.config.import: optional:secret.properties
spring.config.import:
- optional:secret.properties
- categories.yml
renderer:
github:
organization: spring-guides
Expand All @@ -20,6 +22,7 @@ renderer:
gs-rest-service: https://spring.academy/guides/rest-service
gs-spring-boot: https://spring.academy/guides/building-an-application-with-spring-boot
gs-messaging-rabbitmq: https://spring.academy/guides/messaging-with-rabbitmq

spring:
security:
user:
Expand Down
106 changes: 106 additions & 0 deletions src/main/resources/categories.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
renderer:
category:
batch:
display-name: Batch Processing
guide:
- gs-batch-processing
- gs-spring-cloud-task
caching:
display-name: Caching
guide:
- gs-caching
cloud-platform:
display-name: Cloud Platform
guide:
- gs-spring-boot-for-azure
- gs-spring-boot-kubernetes
data:
display-name: Data Access
guide:
- gs-accessing-data-mysql
- gs-accessing-data-jpa
- gs-accessing-data-mongodb
- gs-relational-data-access
- gs-accessing-data-rest
- gs-managing-transactions
- gs-accessing-data-r2dbc
- gs-accessing-mongodb-data-rest
- gs-accessing-data-cassandra
- gs-spring-data-reactive-redis
- gs-accessing-data-neo4j
- gs-accessing-data-gemfire
io:
display-name: IO
guide:
- gs-uploading-files
- gs-async-method
- gs-validating-form-input
- gs-handling-form-submission
- gs-scheduling-tasks
messaging:
display-name: Messaging
guide:
- gs-messaging-stomp-websocket
- gs-messaging-rabbitmq
- gs-messaging-jms
- gs-messaging-gcp-pubsub
- gs-messaging-gcp-pubsub
- gs-integration
- gs-spring-cloud-stream
microservices:
display-name: Microservices
guide:
- gs-gateway
- gs-service-registration-and-discovery
- gs-spring-cloud-loadbalancer
- gs-cloud-circuit-breaker
- gs-centralized-configuration
rest:
display-name: REST APIs
guide:
- gs-accessing-neo4j-data-rest
- gs-accessing-gemfire-data-rest
- gs-rest-hateoas
- gs-reactive-rest-service
- gs-consuming-rest
- gs-rest-service
- gs-spring-boot
security:
display-name: Security
guide:
- gs-accessing-vault
- gs-vault-config
- gs-authenticating-ldap
- gs-rest-service-cors
- gs-securing-web
testing:
display-name: Testing
guide:
- gs-contract-rest
- gs-testing-web
documentation:
display-name: Documentation
guide:
- gs-testing-restdocs
web:
display-name: Web
guide:
- gs-serving-web-content
- gs-producing-web-service
- gs-consuming-web-service
packaging:
display-name: Packaging
guide:
ops:
display-name: Ops
guide:
- gs-actuator-service
streaming:
display-name: Streaming
guide:
- gs-spring-cloud-dataflow
graphql:
display-name: GraphQL
guide:
- gs-graphql-server

10 changes: 6 additions & 4 deletions src/test/java/io/spring/renderer/guides/GuideModelTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package io.spring.renderer.guides;

import java.util.Arrays;
import java.util.Collections;

import io.spring.renderer.github.Repository;
import org.junit.jupiter.api.Test;
Expand All @@ -35,7 +36,7 @@ public void nullRepositoryDescription() {
"git://example.org/spring-guides/gs-sample-guide.git",
"[email protected]:spring-guides/gs-sample-guide.git",
"https://example.org/spring-guides/gs-sample-guide.git", null);
GuideMetadata guideMetadata = new GuideMetadata(repository, null);
GuideMetadata guideMetadata = new GuideMetadata(repository, null, Collections.singleton("test"));
GuideModel guideModel = new GuideModel(guideMetadata);
assertThat(guideModel.getName()).isEqualTo("sample-guide");
assertThat(guideModel.getRepositoryName()).isEqualTo("spring-guides/gs-sample-guide");
Expand All @@ -47,6 +48,7 @@ public void nullRepositoryDescription() {
assertThat(guideModel.getGitUrl()).isEqualTo("git://example.org/spring-guides/gs-sample-guide.git");
assertThat(guideModel.getSshUrl()).isEqualTo("[email protected]:spring-guides/gs-sample-guide.git");
assertThat(guideModel.getProjects()).isEmpty();
assertThat(guideModel.getCategory()).containsExactly("test");
}

@Test
Expand All @@ -56,7 +58,7 @@ public void noGuideProjects() {
"git://example.org/spring-guides/tut-sample-guide.git",
"[email protected]:spring-guides/tut-sample-guide.git",
"https://example.org/spring-guides/tut-sample-guide.git", null);
GuideMetadata guideMetadata = new GuideMetadata(repository, "http://test.academy");
GuideMetadata guideMetadata = new GuideMetadata(repository, "http://test.academy", Collections.emptySet());
GuideModel guideModel = new GuideModel(guideMetadata);
assertThat(guideModel.getName()).isEqualTo("sample-guide");
assertThat(guideModel.getRepositoryName()).isEqualTo("spring-guides/tut-sample-guide");
Expand All @@ -75,7 +77,7 @@ public void withGuideProjects() {
"[email protected]:spring-guides/top-sample-guide.git",
"https://example.org/spring-guides/top-sample-guide.git",
Arrays.asList("spring-framework", "spring-boot"));
GuideMetadata guideMetadata = new GuideMetadata(repository, null);
GuideMetadata guideMetadata = new GuideMetadata(repository, null, Collections.emptySet());
GuideModel guideModel = new GuideModel(guideMetadata);
assertThat(guideModel.getName()).isEqualTo("sample-guide");
assertThat(guideModel.getRepositoryName()).isEqualTo("spring-guides/top-sample-guide");
Expand All @@ -94,7 +96,7 @@ public void deprecatedGuide() {
"[email protected]:spring-guides/deprecated-gs-sample-guide.git",
"https://example.org/spring-guides/deprecated-gs-sample-guide.git",
Arrays.asList("spring-framework", "spring-boot"));
GuideMetadata guideMetadata = new GuideMetadata(repository, null);
GuideMetadata guideMetadata = new GuideMetadata(repository, null, Collections.emptySet());
GuideModel guideModel = new GuideModel(guideMetadata);
assertThat(guideModel.getName()).isEqualTo("deprecated-gs-sample-guide");
assertThat(guideModel.getTitle()).isEqualTo("Title");
Expand Down
Loading

0 comments on commit cd3f130

Please sign in to comment.