Skip to content

Commit

Permalink
minor improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
vondacho committed May 22, 2023
1 parent 7341bfc commit 6301cdf
Show file tree
Hide file tree
Showing 26 changed files with 103 additions and 77 deletions.
12 changes: 5 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,17 @@ CRUD operations on Customer entities exposed by a REST API.

- Web request validation with [Swagger request validator](https://bitbucket.org/atlassian/swagger-request-validator/src/master/)
- Web security based on Basic Authentication
- Exception handling with [Zalando problem handling](https://github.com/zalando/problem-spring-web)
- Application management with Spring Actuator
- Acceptance testing with [Cucumber](https://cucumber.io/docs/cucumber/)
- Contract testing with [Pact](https://docs.pact.io/) and [Spring Cloud Contract](https://softwaremill.com/contract-testing-spring-cloud-contract/)
- Architecture testing with [ArchUnit](https://www.archunit.org/motivation)

## Getting started
- Build the project with `./gradlew clean build`.
- Start the database with `docker-compose up`.
- Launch the application with `./gradlew bootRun --args='--spring.profiles.active=test,jpa,postgres'`.
- Start the containerized database with `docker-compose up`.
- Launch the application locally with `./gradlew bootRun --args='--spring.profiles.active=test,jpa,postgres'`.
- Play use cases with Postman using [this default collection](https://vondacho.github.io/arch-blueprint-java/postman/postman_collection.json) or with [Swagger UI](https://vondacho.github.io/arch-blueprint-java/api/).

## Technical documentation
- Powered by [MkDocs](https://www.mkdocs.org/getting-started/)
- API documentation powered by [Swagger UI](https://swagger.io/tools/swagger-ui/)
- Architecture documentation powered by [Structurizr](https://structurizr.com/) and [AppMap](https://appmap.io/docs/appmap-overview.html)
- [Latest release](https://vondacho.github.io/arch-blueprint-java)
## Documentation
Find full detailed documentation [here](https://vondacho.github.io/arch-blueprint-java/) powered by [MkDocs](https://www.mkdocs.org/getting-started/)
File renamed without changes.
14 changes: 7 additions & 7 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import ru.vyarus.gradle.plugin.python.PythonExtension
plugins {
id("java")
id("org.springframework.boot") version "2.7.10"
id("io.spring.dependency-management") version "1.0.15.RELEASE"
id("org.springframework.cloud.contract") version "3.1.6"
id("au.com.dius.pact") version "4.5.5"
id("io.spring.dependency-management") version "1.0.15.RELEASE"
id("io.qameta.allure-aggregate-report") version "2.11.2"
id("com.appland.appmap") version "1.1.1"
id("org.hidetake.swagger.generator") version "2.19.2"
Expand Down Expand Up @@ -133,7 +133,7 @@ allure {
}

appmap {
configFile.set(file("$projectDir/src/doc/appmap.yml"))
configFile.set(file("$projectDir/appmap.yml"))
outputDirectory.set(file("$buildDir/appmap"))
isSkip = false
debug = "info"
Expand All @@ -153,13 +153,13 @@ plantuml {
}
diagrams {
create("hexagonal") {
sourceFile = project.file("src/doc/uml/hexagonal.puml")
sourceFile = project.file("doc/uml/hexagonal.puml")
}
create("domain-model") {
sourceFile = project.file("src/doc/uml/domain-model.puml")
sourceFile = project.file("doc/uml/domain-model.puml")
}
create("data-model") {
sourceFile = project.file("src/doc/uml/data-model.puml")
sourceFile = project.file("doc/uml/data-model.puml")
}
}
}
Expand All @@ -171,7 +171,7 @@ python {
mkdocs {
strict = false
updateSiteUrl = false
sourcesDir = "src/doc"
sourcesDir = "doc"
buildDir = "build/mkdocs"
publish.docPath = ""
}
Expand All @@ -188,7 +188,7 @@ gitPublish {
from("build/swagger-ui-apidoc") {
into("api")
}
from("src/doc/postman") {
from("doc/postman") {
into("postman")
}
from("build/appmap/junit") {
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
1 change: 1 addition & 0 deletions src/doc/tech/index.md → doc/tech/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ CRUD operations on Customer entities exposed by a REST API.

- Web request validation with [Swagger request validator](https://bitbucket.org/atlassian/swagger-request-validator/src/master/)
- Web security based on Basic Authentication
- Exception handling with [Zalando problem handling](https://github.com/zalando/problem-spring-web)
- Application management with Spring Actuator
- Acceptance testing with [Cucumber](https://cucumber.io/docs/cucumber/)
- Contract testing with [Pact](https://docs.pact.io/) and [Spring Cloud Contract](https://softwaremill.com/contract-testing-spring-cloud-contract/)
Expand Down
21 changes: 17 additions & 4 deletions src/doc/tech/miscellaneous.md → doc/tech/miscellaneous.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,23 @@
## This documentation
Build technical documentation with `./gradlew allureAggregateReport plantumlAll generateSwaggerUI mkdocsBuild`.

## AppMap resources
Generate AppMap resources with `./gradlew appmap test`.

View your local AppMap resources following [this tutorial](https://github.com/vondacho/appmap-viewer#getting-started).
## Publication to Structurizr cloud
- To have a free account on https://structurizr.com/help/getting-started
- To run:
```
java \
-cp ./build/libs/arch-blueprint-java.jar \
-Dloader.main=edu.obya.blueprint.customer.c4.BlueprintC4Model \
org.springframework.boot.loader.PropertiesLauncher \
./src/c4/c4-blueprint-java.dsl \
<workspace-id>
```
- To visualize your architecture model at https://structurizr.com/

## Map components flows
Record components flows with `./gradlew appmap test`.

Then, view your local AppMap JSON resources following [this tutorial](https://github.com/vondacho/appmap-viewer#getting-started).

## Release
Draft new release of the application from GitHub [release panel](https://github.com/vondacho/arch-blueprint-java/releases).
Expand Down
File renamed without changes.
2 changes: 0 additions & 2 deletions src/doc/tech/testing.md → doc/tech/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ Launch the whole tests suite with `./gradlew clean check`.
## Unit and integration testing
Launch the unit and integration tests with `./gradlew test`.

Record component interaction scenarios with `./gradlew appmap test`.

## Acceptance testing
Launch the acceptance tests with `./gradlew acceptanceTest`.

Expand Down
File renamed without changes.
5 changes: 0 additions & 5 deletions src/doc/uml/domain-model.puml → doc/uml/domain-model.puml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,6 @@

!includeurl https://raw.githubusercontent.com/skleanthous/C4-PlantumlSkin/master/build/output/general/notes.puml

class Customer {
CustomerId id
CustomerState state
}

Customer *--> "1" CustomerId
Customer *--> "1" CustomerState

Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ Feature: To create one new customer
| <first-name> | <last-name> |
Then the response status is BAD_REQUEST
And the set of existing customers is left unchanged

Examples:
| first-name | last-name |
| | |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ Feature: To update an existing customer
| <first-name> | <last-name> |
Then the response status is BAD_REQUEST
And the set of existing customers is left unchanged

Examples:
| id | first-name | last-name |
| ce751f30-217a-422c-b81b-8f75df4917b6 | | |
Expand Down
4 changes: 1 addition & 3 deletions src/archTest/resources/archunit_ignore_patterns.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
# Because Spring context configuration depends on JPA adapter configuration
.*CustomerEndpointIT.*
# Because Spring context configuration depends on JPA adapter configuration
.*CustomerServiceIT.*
.*IT.*
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ public class BlueprintC4Model {
public static void main(String... args) {
if (args.length >= 1) {
try {
val dslPath = args[0];
val workspace = getWorkspaceFromDslFile(dslPath);
val workspace = getWorkspaceFromDslFile(args[0]);

// find predefined blueprint api container
val system = workspace.getModel().getSoftwareSystemWithName("blueprint-system");
Expand All @@ -42,13 +41,13 @@ public static void main(String... args) {
"blueprint-api-components",
"The components inside the Blueprint API container"
);
componentView.setExternalSoftwareSystemBoundariesVisible(true);
componentView.addAllElements();

WorkspaceUtils.printWorkspaceAsJson(workspace);

if (args.length > 1) {
val workspaceId = Long.valueOf(args[1]);
uploadWorkspace(workspace, workspaceId);
uploadWorkspace(workspace, Long.valueOf(args[1]));
}

} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ workspace "Obya" "A set of system architectures represented with the C4 model" {
dbServer = container "database-server" "Manages database instances" "PostgreSQL" "infrastructure,database" {
dbInstance = component "database" "Stores customer data" "PostgreSQL" "infrastructure,schema"
}
}
}
# internal persons
user = person "User" "API client"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,20 @@

import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.springframework.util.Base64Utils;

import java.nio.charset.StandardCharsets;

import static edu.obya.blueprint.customer.adapter.rest.TestWebUser.*;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class TestWebUserTokens {
public static final String TEST_USER_TOKEN = "Basic dGVzdDp0ZXN0";
public static final String TEST_ADMIN_TOKEN = "Basic YWRtaW46YWRtaW4=";
public static final String TEST_USER_TOKEN = tokenOf(TEST_USER_NAME, TEST_USER_PASSWORD);
public static final String TEST_ADMIN_TOKEN = tokenOf(TEST_ADMIN_NAME, TEST_ADMIN_PASSWORD);

private static String tokenOf(String username, String password) {
return String.format("Basic %s",
Base64Utils.encodeToString(
String.format("%s:%s", username, password).getBytes(StandardCharsets.UTF_8)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ public V4Pact getCustomer(PactBuilder builder) {
.uponReceiving("get existing customer interaction")
.method("GET")
.matchPath(URI_WITH_ID_REGEX, String.format("/customers/%s", TEST_CUSTOMER_ID))
.matchHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE, APPLICATION_JSON_VALUE)
.matchHeader(AUTHORIZATION, BASIC_AUTH_REGEX, TEST_USER_TOKEN)
.willRespondWith()
.status(200)
.matchHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE, APPLICATION_JSON_VALUE)
.body(newJsonBody(object -> {
object.uuid("id", TEST_CUSTOMER_ID.getId());
object.stringType("firstName", TEST_CUSTOMER_OUT.getFirstName());
Expand All @@ -66,11 +66,12 @@ public V4Pact addCustomer(PactBuilder builder) {
.matchHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE, APPLICATION_JSON_VALUE)
.matchHeader(AUTHORIZATION, BASIC_AUTH_REGEX, TEST_USER_TOKEN)
.body(newJsonBody(object -> {
object.stringType("firstName", TEST_CUSTOMER_STATE.getFirstName());
object.stringType("lastName", TEST_CUSTOMER_STATE.getLastName());
object.stringType("firstName", TEST_CUSTOMER_IN.getFirstName());
object.stringType("lastName", TEST_CUSTOMER_IN.getLastName());
}).build())
.willRespondWith()
.status(201)
.matchHeader(CONTENT_TYPE, TEXT_PLAIN_VALUE, TEXT_PLAIN_VALUE)
.body(TEST_CUSTOMER_ID.getId().toString(), TEXT_PLAIN_VALUE)
.toPact()
.asV4Pact().get();
Expand All @@ -87,8 +88,8 @@ public V4Pact replaceCustomer(PactBuilder builder) {
.matchHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE, APPLICATION_JSON_VALUE)
.matchHeader(AUTHORIZATION, BASIC_AUTH_REGEX, TEST_USER_TOKEN)
.body(newJsonBody(object -> {
object.stringType("firstName", TEST_CUSTOMER_STATE.getFirstName());
object.stringType("lastName", TEST_CUSTOMER_STATE.getLastName());
object.stringType("firstName", TEST_CUSTOMER_IN.getFirstName());
object.stringType("lastName", TEST_CUSTOMER_IN.getLastName());
}).build())
.willRespondWith()
.status(204)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@
"headers": {
"Authorization": [
"Basic dGVzdDp0ZXN0"
],
"Content-Type": [
"application/json"
]
},
"matchingRules": {
Expand All @@ -36,15 +33,6 @@
"regex": "Basic (?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?"
}
]
},
"Content-Type": {
"combine": "AND",
"matchers": [
{
"match": "regex",
"regex": "application/json"
}
]
}
},
"path": {
Expand All @@ -68,12 +56,12 @@
"id": "64a0f7d1-7b25-412d-b1e0-abacde3c21cd",
"lastName": "Doe"
},
"contentType": "application/json; charset=UTF-8",
"contentType": "application/json",
"encoded": false
},
"headers": {
"Content-Type": [
"application/json; charset=UTF-8"
"application/json"
]
},
"matchingRules": {
Expand Down Expand Up @@ -118,7 +106,7 @@
"matchers": [
{
"match": "regex",
"regex": "application/json(;\\s?charset=[\\w\\-]+)?"
"regex": "application/json"
}
]
}
Expand Down Expand Up @@ -355,6 +343,19 @@
"text/plain"
]
},
"matchingRules": {
"header": {
"Content-Type": {
"combine": "AND",
"matchers": [
{
"match": "regex",
"regex": "text/plain"
}
]
}
}
},
"status": 201
},
"transport": "https",
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/edu/obya/blueprint/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration;

import java.time.ZoneOffset;
import java.util.TimeZone;

@EnableAutoConfiguration(exclude = ErrorMvcAutoConfiguration.class)
@SpringBootApplication
public class Application {
public static void main(String[] args) {
TimeZone.setDefault(TimeZone.getTimeZone(ZoneOffset.UTC));
SpringApplication.run(Application.class, args);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.cors(withDefaults())
.authorizeHttpRequests((authz) -> authz
.mvcMatchers("/customers", "/customers/**").hasAnyRole("USER","ADMIN")
.mvcMatchers("/customers", "/customers/**").hasRole("USER")
.requestMatchers(EndpointRequest.to("health","info","metrics","loggers")).permitAll()
.requestMatchers(EndpointRequest.toAnyEndpoint()).hasAnyRole("ADMIN")
.anyRequest()
Expand Down Expand Up @@ -72,7 +72,7 @@ public UserDetailsService devUserDetailsService() {
User.builder()
.username("admin")
.password(passwordEncoder.encode("admin"))
.roles("ADMIN")
.roles("USER", "ADMIN")
.build()
);
}
Expand All @@ -95,7 +95,7 @@ public UserDetailsService testUserDetailsService() {
User.builder()
.username("admin")
.password(passwordEncoder.encode("admin"))
.roles("ADMIN")
.roles("USER", "ADMIN")
.build()
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.servlet.Filter;
import java.io.IOException;

@Configuration
public class WebValidationConfiguration {
Expand All @@ -23,7 +22,7 @@ public Filter validationFilter() {
}

@Bean
public WebMvcConfigurer addOpenApiValidationInterceptor(OpenApiValidationInterceptor interceptor) throws IOException {
public WebMvcConfigurer addOpenApiValidationInterceptor(OpenApiValidationInterceptor interceptor) {
return new WebMvcConfigurer() {
@Override
public void addInterceptors(final @NonNull InterceptorRegistry registry) {
Expand Down
Loading

0 comments on commit 6301cdf

Please sign in to comment.