Skip to content

Commit 961ab2a

Browse files
authored
Migrate to Antora
1 parent 568bba3 commit 961ab2a

29 files changed

+3849
-1
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
name: Build and Publish Antora Site
2+
3+
on:
4+
push:
5+
branches:
6+
- asciidoc/antora
7+
paths:
8+
- 'docs/**'
9+
- 'antora-playbook.yml'
10+
- '.github/workflows/build-and-publish-antora.yml'
11+
- '**/README.adoc'
12+
workflow_dispatch:
13+
14+
jobs:
15+
build:
16+
runs-on: ubuntu-latest
17+
steps:
18+
- name: Checkout repository
19+
uses: actions/checkout@v4
20+
21+
- name: Setup Node.js
22+
uses: actions/setup-node@v4
23+
with:
24+
node-version: '18'
25+
26+
- name: Install Antora
27+
run: npm i -g @antora/cli@3.1 @antora/site-generator@3.1
28+
29+
- name: Generate site
30+
run: antora --fetch antora-playbook.yml
31+
32+
- name: Upload site as artifact
33+
uses: actions/upload-artifact@v4
34+
with:
35+
name: site
36+
path: build/site
37+
38+
deploy:
39+
needs: build
40+
runs-on: ubuntu-latest
41+
permissions:
42+
contents: read
43+
pages: write
44+
id-token: write
45+
environment:
46+
name: github-pages
47+
url: ${{ steps.deployment.outputs.page_url }}
48+
steps:
49+
- name: Download site artifact
50+
uses: actions/download-artifact@v4
51+
with:
52+
name: site
53+
path: build/site
54+
55+
- name: Setup Pages
56+
uses: actions/configure-pages@v4
57+
58+
- name: Upload Pages artifact
59+
uses: actions/upload-pages-artifact@v3
60+
with:
61+
path: build/site
62+
63+
- name: Deploy to GitHub Pages
64+
id: deployment
65+
uses: actions/deploy-pages@v4

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ out/
3535

3636
### VS Code ###
3737
.vscode/
38-
3938
### Ignore Gradle files in submodules ###
4039
**/gradlew*
4140
**/gradle/
41+
### Antora ###
42+
build/site/

README.adoc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
:nofooter:
33
:icons: font
44
:url-quickref: https://github.com/rashidi/spring-boot-tutorials
5+
:url-docs: https://rashidi.github.io/spring-boot-tutorials
56

67
Samples and tutorials for https://spring.io/projects/spring-boot[Spring Boot] modules such as, but not limited to,
78
Spring Data, Spring Batch, Spring Cloud, Spring Security, Spring GraphQL, and Spring Test. Each tutorial is equipped
@@ -13,6 +14,7 @@ image:https://img.shields.io/github/license/rashidi/spring-boot-tutorials?style=
1314
image:https://img.shields.io/github/actions/workflow/status/rashidi/spring-boot-tutorials/build-and-publish.yml?style=flat-square&logo=githubactions&color=blue[GitHub Actions Workflow Status]
1415
image:https://img.shields.io/sonar/coverage/rashidi_spring-boot-tutorials?server=https%3A%2F%2Fsonarcloud.io&style=flat-square&color=blue[Sonar Coverage]
1516
image:https://img.shields.io/github/stars/rashidi/spring-boot-tutorials?style=flat-square&logo=github[GitHub Repo stars, link={url-quickref}]
17+
image:https://img.shields.io/github/actions/workflow/status/rashidi/spring-boot-tutorials/build-and-publish-antora.yml?style=flat-square&logo=antora&label=Antora&color=blue[Antora Site Status, link={url-docs}]
1618

1719
== Motivation
1820

@@ -30,6 +32,10 @@ With the help of https://github.com/dependabot[Dependabot], each tutorial is als
3032
dependencies. Currently, we are using Java https://adoptium.net/en-GB/temurin/releases/?version=21[Temurin 21]
3133
with https://plugins.gradle.org/plugin/org.springframework.boot/3.4.3[Spring Boot 3.4.3].
3234

35+
== Documentation
36+
37+
All tutorials are documented in AsciiDoc format and published as an https://antora.org/[Antora] site at {url-docs}[Spring Boot Tutorials]. The documentation is automatically generated and published whenever changes are made to the documentation files or the tutorials themselves.
38+
3339
== Topics
3440

3541
|===

antora-playbook.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
site:
2+
title: Spring Boot Tutorials
3+
url: https://rashidi.github.io/spring-boot-tutorials
4+
start_page: spring-boot-tutorials::index.adoc
5+
6+
content:
7+
sources:
8+
- url: .
9+
start_path: docs
10+
11+
ui:
12+
bundle:
13+
url: https://gitlab.com/antora/antora-ui-default/-/jobs/artifacts/HEAD/raw/build/ui-bundle.zip?job=bundle-stable
14+
snapshot: true
15+
16+
runtime:
17+
fetch: true

docs/antora.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
name: spring-boot-tutorials
2+
title: Spring Boot Tutorials
3+
version: master
4+
nav:
5+
- modules/ROOT/nav.adoc

docs/modules/ROOT/nav.adoc

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
* xref:index.adoc[Home]
2+
* Spring Batch
3+
** xref:batch-rest-repository.adoc[Batch Job With REST API source]
4+
** xref:batch-skip-step.adoc[Skip Specific Data]
5+
* Spring Cloud
6+
** xref:cloud-jdbc-env-repo.adoc[Environment Repository]
7+
* Spring Data
8+
** xref:data-domain-events.adoc[Domain Events]
9+
** xref:data-envers-audit.adoc[Envers Audit]
10+
** xref:data-jdbc-audit.adoc[JDBC Audit]
11+
** xref:data-jpa-audit.adoc[JPA Audit]
12+
** xref:data-jpa-event.adoc[Event Driven]
13+
** xref:data-jpa-filtered-query.adoc[Global Filtered Query]
14+
** xref:data-mongodb-audit.adoc[MongoDB Audit]
15+
** xref:data-mongodb-full-text-search.adoc[MongoDB Full Text Search]
16+
** xref:data-mongodb-transactional.adoc[MongoDB Transactional]
17+
** xref:data-repository-definition.adoc[Repository Definition]
18+
** xref:data-rest-validation.adoc[REST Validation]
19+
* Spring GraphQL
20+
** xref:graphql.adoc[GraphQL Server]
21+
* jOOQ
22+
** xref:jooq.adoc[jOOQ]
23+
* Spring Test
24+
** xref:data-mongodb-tc-data-load.adoc[Load data with Testcontainers]
25+
** xref:test-execution-listeners.adoc[Test Execution Listeners]
26+
** xref:test-rest-assured.adoc[Integration with RestAssured]
27+
** xref:test-slice-tests-rest.adoc[Implementing Slice Tests for REST application]
28+
* Spring Web
29+
** xref:web-rest-client.adoc[REST Clients for calling Synchronous API]
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
= Spring Batch: Working With REST Resources
2+
:source-highlighter: highlight.js
3+
:toc:
4+
:nofooter:
5+
:icons: font
6+
:url-quickref: https://github.com/rashidi/spring-boot-tutorials/tree/master/batch-rest-repository
7+
8+
Implement batch operation for REST resources with https://spring.io/projects/spring-batch[Spring Batch]
9+
10+
11+
== Background
12+
Spring Batch allows us to perform large volumes of records from several resources such as https://docs.spring.io/spring-batch/docs/current/api/org/springframework/batch/item/file/FlatFileItemReader.html[File],
13+
https://docs.spring.io/spring-batch/docs/current/api/org/springframework/batch/item/database/JpaPagingItemReader.html[Relational Database], and,
14+
https://docs.spring.io/spring-batch/docs/current/api/org/springframework/batch/item/json/JsonItemReader.html[JSON file] to name a few.
15+
16+
In this article, we will explore how to implement batch operation that reads from REST resources with Spring Batch through `JsonItemReader`. We will retrieve a list of users from https://jsonplaceholder.typicode.com/users[JSON Placeholder] and save them into a database.
17+
18+
== Job Configuration
19+
Next is to implement the job that will be responsible to read from REST resource and save them into a database. `Job` consists of `Step` and `Step`
20+
consists of `ItemReader` and `ItemWriter`. We will implement all of them in link:{url-quickref}/src/main/java/zin/rashidi/boot/batch/rest/user/UserJobConfiguration.java[UserJobConfiguration].
21+
22+
[source,java]
23+
----
24+
@Configuration
25+
class UserJobConfiguration {
26+
private final JobRepository jobRepository;
27+
private final PlatformTransactionManager transactionManager;
28+
private final MongoOperations mongo;
29+
UserJobConfiguration(JobRepository jobRepository, PlatformTransactionManager transactionManager, MongoOperations mongo) {
30+
this.jobRepository = jobRepository;
31+
this.transactionManager = transactionManager;
32+
this.mongo = mongo;
33+
}
34+
@Bean
35+
public Job userJob() throws MalformedURLException {
36+
return new JobBuilder("userJob", jobRepository).start(step()).build();
37+
}
38+
private Step step() throws MalformedURLException {
39+
return new StepBuilder("userStep", jobRepository)
40+
.<User, User>chunk(10, transactionManager)
41+
.reader(reader())
42+
.writer(writer())
43+
.build();
44+
}
45+
private JsonItemReader<User> reader() throws MalformedURLException {
46+
JacksonJsonObjectReader<User> jsonObjectReader = new JacksonJsonObjectReader<>(User.class);
47+
jsonObjectReader.setMapper(new ObjectMapper());
48+
return new JsonItemReaderBuilder<User>()
49+
.name("userReader")
50+
.jsonObjectReader(jsonObjectReader)
51+
.resource(new UrlResource("https://jsonplaceholder.typicode.com/users"))
52+
.build();
53+
}
54+
private MongoItemWriter<User> writer() {
55+
return new MongoItemWriterBuilder<User>()
56+
.template(mongo)
57+
.build();
58+
}
59+
}
60+
----
61+
62+
From the code above, we can see that a `URL` form of `Resource` is assigned to `JsonItemReader`. We will depend on `JacksonJsonObjectRader` to convert response from link:https://jsonplaceholder.typicode.com/users[JSON Placeholder] to `User` object.
63+
64+
[source,java]
65+
----
66+
@Configuration
67+
class UserJobConfiguration {
68+
private JsonItemReader<User> reader() throws MalformedURLException {
69+
JacksonJsonObjectReader<User> jsonObjectReader = new JacksonJsonObjectReader<>(User.class);
70+
jsonObjectReader.setMapper(new ObjectMapper());
71+
return new JsonItemReaderBuilder<User>()
72+
.name("userReader")
73+
.jsonObjectReader(jsonObjectReader)
74+
.resource(new UrlResource("https://jsonplaceholder.typicode.com/users"))
75+
.build();
76+
}
77+
}
78+
----
79+
80+
Now that we have implemented the `Job`, we can verify that it is working by executing an integration test.
81+
82+
== Verification
83+
We will launch `userJob` which will retrieve list of `User` from https://jsonplaceholder.typicode.com/users[JSON Placeholder] and save them into a database.
84+
Once completed then we will verify that the database contains the expected number of users.
85+
86+
[source,java]
87+
----
88+
@Testcontainers
89+
@SpringBatchTest
90+
@SpringBootTest(classes = { BatchTestConfiguration.class, MongoTestConfiguration.class, UserJobConfiguration.class }, webEnvironment = NONE)
91+
class UserBatchJobTests {
92+
@Container
93+
@ServiceConnection
94+
private final static MySQLContainer<?> MYSQL_CONTAINER = new MySQLContainer<>("mysql:latest")
95+
.withInitScript("org/springframework/batch/core/schema-mysql.sql");
96+
@Container
97+
@ServiceConnection
98+
private final static MongoDBContainer MONGO_DB_CONTAINER = new MongoDBContainer("mongo:latest");
99+
@Autowired
100+
private JobLauncherTestUtils launcher;
101+
@Autowired
102+
private MongoOperations mongoOperations;
103+
@Test
104+
@DisplayName("Given there are 10 users returned from REST Service When the job is COMPLETED Then all users should be saved to MongoDB")
105+
void launch() {
106+
await().atMost(ofSeconds(30)).untilAsserted(() -> {
107+
var execution = launcher.launchJob();
108+
assertThat(execution.getExitStatus()).isEqualTo(COMPLETED);
109+
});
110+
var persistedUsers = mongoOperations.findAll(User.class);
111+
assertThat(persistedUsers).hasSize(10);
112+
}
113+
}
114+
----
115+
116+
Full implementation can be found in link:{url-quickref}/src/test/java/zin/rashidi/boot/batch/rest/user/UserBatchJobTests.java[UserBatchJobTests].

0 commit comments

Comments
 (0)