Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ plugins {
id 'org.springframework.boot' version '3.0.1'
id 'io.spring.dependency-management' version '1.1.0'
id 'java'
id 'groovy'
}

targetCompatibility = '17'
Expand All @@ -14,6 +15,8 @@ repositories {
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation('org.springframework.boot:spring-boot-starter-test')
testImplementation 'org.spockframework:spock-core:2.4-M1-groovy-4.0'
testImplementation 'org.spockframework:spock-spring:2.4-M1-groovy-4.0'
}

test {
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/com/wilkins/showcase/ShowcaseApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,22 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.annotation.Bean;

import java.util.UUID;
import java.util.function.Supplier;

@ServletComponentScan
@SpringBootApplication
public class ShowcaseApplication {

public static void main(String[] args) {
SpringApplication.run(ShowcaseApplication.class, args);
}

@Bean
public Supplier<String> randomStringSupplier() {
return () -> UUID.randomUUID().toString();
}
}
26 changes: 26 additions & 0 deletions src/main/java/com/wilkins/showcase/filters/TraceIdFilter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.wilkins.showcase.filters;

import com.wilkins.showcase.service.ExampleService;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;

import java.io.IOException;

@WebFilter("/*")
public class TraceIdFilter implements Filter {

private final ExampleService idService;

public TraceIdFilter(ExampleService idService) {
this.idService = idService;
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.setHeader("x-trace-id", idService.findExampleId());
chain.doFilter(request, response);
}
}
10 changes: 10 additions & 0 deletions src/main/java/com/wilkins/showcase/service/Example.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.wilkins.showcase.service;

import java.util.List;

public record Example (
String key,
String value,
String id,
List<String> parts) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.wilkins.showcase.service;

public interface ExampleService {
String findExampleId();
Example createExample();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.wilkins.showcase.service;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.function.Supplier;

@Service
public class GreatExampleService implements ExampleService {

private final String prefix;
private final Supplier<String> supplier;
private final String joiner;

public GreatExampleService(@Value("${showcase.id-service.prefix}") String prefix,
Supplier<String> supplier,
@Value("${showcase.id-service.joiner}") String joiner) {
this.prefix = prefix;
this.supplier = supplier;
this.joiner = joiner;
}

@Override
public String findExampleId() {
return prefix + joiner + supplier.get();
}

@Override
public Example createExample() {
var exampleId = findExampleId();
var suppliedExample = supplier.get();
return new Example(prefix, suppliedExample, exampleId, List.of(prefix, suppliedExample, exampleId));
}
}
4 changes: 4 additions & 0 deletions src/main/resources/application.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
showcase:
id-service:
prefix: "showcase"
joiner: "::"
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.wilkins.showcase.controllers

import com.wilkins.showcase.service.ExampleService
import org.spockframework.spring.SpringBean
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.web.client.TestRestTemplate
import spock.lang.Specification
import spock.lang.Unroll

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class GreetingControllerSpec extends Specification {

@Autowired
TestRestTemplate restTemplate;

@SpringBean
ExampleService exampleService = Mock()

@Unroll
def "should wish #providedName an appropriate greeting"() {
given:
exampleService.findExampleId() >> "mock-id"

when:
def response= restTemplate
.getForEntity("/greeting?name={name}&salutation={salutation}", Greeting.class, providedName, providedSalutation)

then:
response.statusCode.value() == 200
response.headers.getFirst('x-trace-id') == 'mock-id'
with(response.body) {
salutation == providedSalutation
name == providedName
}

where:
providedName | providedSalutation
'world' | 'hello'
'robin' | "good afternoon"
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.wilkins.showcase.service

import spock.lang.Specification
import spock.lang.Unroll

import java.util.function.Supplier

class GreatExampleServiceSpec extends Specification {

Supplier<String> stringSupplier = Mock()

@Unroll
def "ID String is generated correctly when using the GreatExampleService"() {
given:
stringSupplier.get() >> suppliedString
def idService = new GreatExampleService(something, stringSupplier, joiner)

expect:
expectedId == idService.findExampleId()

where:
something | suppliedString | joiner || expectedId
"abc" | "123" | "::" || "abc::123"
"1" | "2" | "||" || "1||2"
}

@Unroll
def "Example is generated correctly when using the key: #prefix and suppliedString: #suppliedString"() {
given:
stringSupplier.get() >> suppliedString
def exampleService = new GreatExampleService(prefix, stringSupplier, joiner)

when:
def example = exampleService.createExample()

then:
with(example) {
key == prefix
value == suppliedString
id == expectedId
parts == [prefix, suppliedString, expectedId] as List
}

where:
prefix | suppliedString | joiner || expectedId
"abc" | "123" | "::" || "abc::123"
"1" | "2" | "//" || "1//2"

}
}