Skip to content
This repository has been archived by the owner on Jul 26, 2024. It is now read-only.

Commit

Permalink
Merge pull request #11 from ralf-ueberfuhr-ars/features/config
Browse files Browse the repository at this point in the history
Introduce config for CORS and refactor in-memory implementation of blogpost service
  • Loading branch information
ralf-ueberfuhr-ars authored Dec 21, 2023
2 parents b291ff1 + 59e421a commit a81c929
Show file tree
Hide file tree
Showing 9 changed files with 178 additions and 16 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package de.sample.schulung.spring.blog.boundary.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import static java.util.Arrays.stream;

@SuppressWarnings("NullableProblems")
@Configuration
public class CorsConfiguration {

@Bean
public WebMvcConfigurer corsConfigurer(final CorsConfigurationData allowed) {
return new WebMvcConfigurer() {

@Override
public void addCorsMappings(final CorsRegistry registry) {
registry.addMapping("/**")
.exposedHeaders(
HttpHeaders.LOCATION,
HttpHeaders.LINK
)
// allow all HTTP request methods
.allowedMethods(
stream(RequestMethod.values())
.map(Enum::name)
.toArray(String[]::new)
)
// allow the commonly used headers
.allowedHeaders(
HttpHeaders.ORIGIN,
HttpHeaders.CONTENT_TYPE,
HttpHeaders.CONTENT_LANGUAGE,
HttpHeaders.ACCEPT,
HttpHeaders.ACCEPT_LANGUAGE,
HttpHeaders.IF_MATCH,
HttpHeaders.IF_NONE_MATCH
)
// this is stage specific
.allowedOrigins(allowed.getOrigins())
.allowCredentials(allowed.isCredentials());
}
};
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package de.sample.schulung.spring.blog.boundary.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

/**
* The properties from application.yml. You can specify them by the following snippet:
*
* <pre>
* server:
* endpoints:
* api:
* v1: /api/v1
* </pre>
*/
@Configuration
@ConfigurationProperties(prefix = "cors.allow")
@Data
public class CorsConfigurationData {

private String[] origins = { "*" };
private boolean credentials = false;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package de.sample.schulung.spring.blog.boundary.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@SuppressWarnings("NullableProblems")
@Configuration
public class IndexPageConfiguration {

@Bean
WebMvcConfigurer indexPageConfig() {
return new WebMvcConfigurer() {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry
.addViewController("/")
.setViewName("redirect:/index.html");
}
};
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Stream;
Expand All @@ -19,28 +16,23 @@
@RequiredArgsConstructor
public class BlogPostService {

private final Map<UUID, BlogPost> blogPosts = new HashMap<>();
private final ApplicationEventPublisher eventPublisher;
private final BlogPostSink sink;

public long count() {
return blogPosts.size();
return sink.count();
}

public Stream<BlogPost> findAll() {
return blogPosts.values().stream();
return sink.findAll();
}

public Optional<BlogPost> findById(UUID id) {
return Optional.ofNullable(
this.blogPosts.get(id)
);
return sink.findById(id);
}

public void create(@Valid @NotNull BlogPost blogPost) {
final var id = UUID.randomUUID();
blogPost.setId(id);
blogPost.setTimestamp(LocalDateTime.now());
this.blogPosts.put(id, blogPost);
this.sink.create(blogPost);
this.eventPublisher.publishEvent(new BlogPostCreatedEvent(blogPost));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package de.sample.schulung.spring.blog.domain;

import java.util.Optional;
import java.util.UUID;
import java.util.stream.Stream;

public interface BlogPostSink {

long count();
void create(BlogPost post);
Stream<BlogPost> findAll();
Optional<BlogPost> findById(UUID id);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package de.sample.schulung.spring.blog.domain;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Stream;

public class InMemoryBlogPostSink implements BlogPostSink {

private final Map<UUID, BlogPost> blogPosts = new HashMap<>();

@Override
public long count() {
return this.blogPosts.size();
}

@Override
public void create(BlogPost post) {
final var id = UUID.randomUUID();
post.setId(id);
post.setTimestamp(LocalDateTime.now());
this.blogPosts.put(id, post);

}

@Override
public Stream<BlogPost> findAll() {
return blogPosts.values().stream();
}

@Override
public Optional<BlogPost> findById(UUID id) {
return Optional.ofNullable(
this.blogPosts.get(id)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package de.sample.schulung.spring.blog.domain;

import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class InMemoryBlogPostSinkConfiguration {

@Bean
@ConditionalOnMissingBean(BlogPostSink.class)
BlogPostSink inMemorySink() {
return new InMemoryBlogPostSink();
}

}
5 changes: 3 additions & 2 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ application:
content: This is a great blog :)
- title: BlogPost 2
content: Das ist ein weiterer Blog Post


cors:
allow:
origins: ${CORS_ALLOW_ORIGINS:*}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package de.sample.schulung.spring.blog.boundary;

import de.sample.schulung.spring.blog.domain.BlogPostService;
import de.sample.schulung.spring.blog.domain.InMemoryBlogPostSink;
import org.junit.jupiter.api.Test;
import org.mapstruct.factory.Mappers;
import org.mockito.Mockito;
Expand All @@ -14,7 +15,7 @@ public class BlogPostControllerTests {
@Test
void shouldCreateBlogPostSuccessfully() {
final var eventPublisher = Mockito.mock(ApplicationEventPublisher.class);
final var service = new BlogPostService(eventPublisher);
final var service = new BlogPostService(eventPublisher, new InMemoryBlogPostSink());
final var mapper = Mappers.getMapper(BlogPostDtoMapper.class);
final var controller = new BlogPostController(service, mapper);
final var blogPost = new BlogPostDto();
Expand Down

0 comments on commit a81c929

Please sign in to comment.