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
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,8 @@

# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*

# Eclipse
.classpath
.project
.settings
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ Please follow the steps below:
Payara Micro is enough to run
3. Run the command: "mvn install" in the root folder of the distributionfolder.
4. Run the blog-web
> java -jar payara-micro-4.1.152.1.jar --deploy blog-web\target\blog-web.war
> java -jar payara-micro-5.2021.8.jar --deploy blog-web\target\blog-web.war
5. Go to address: http://localhost:8080/blog-web/hello-pierce. The server should respond with a text that reads: {"message":"Hello Pierce"}.
6. Now basic environment and Blog Ping project is installed correctly.

Expand All @@ -214,7 +214,7 @@ Please follow the steps below:
The integration tests is (and should be) located under the maven artifact "integration test".
These tests should test the expected behavior of a correct implementation of Blog posts.

There is a simple test to start with. You run the test with "mvn test-Dtest =*TestIntegr" .
There is a simple test to start with. You run the test with "mvn test -Dtest=*TestIntegr" .

The tests and flow is a suggestion and may not use the most convinient APIs to do it, you may change.
You should fill in with extra tests (and expect that we do to ;) )
1 change: 1 addition & 0 deletions blog-web/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/target/
63 changes: 57 additions & 6 deletions blog-web/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<version>1.0-SNAPSHOT</version>
</parent>

<groupId>com.pierceecom.sample</groupId>
<groupId>com.pierceecom.sample.web</groupId>
<artifactId>blog-web</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
Expand All @@ -19,6 +19,21 @@
</properties>

<dependencies>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.6.2</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-jaxrs</artifactId>
<version>1.6.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.13.0</version>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
Expand All @@ -30,11 +45,22 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>2.17</version>
<scope>test</scope>
</dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
<version>1.12</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>4.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<version>2.2</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand All @@ -58,6 +84,31 @@
</configuration>
</plugin>

<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>5.2.0</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${project.basedir}/src/main/java/com/pierceecom/resources/PostsApi.yaml</inputSpec>
<generatorName>jaxrs-resteasy-eap</generatorName>
<output>${project.build.directory}/generated-sources</output>
<apiPackage>com.pierceecom.blog.swagger</apiPackage>
<modelPackage>com.pierceecom.blog.model</modelPackage>
<generateAliasAsModel>true</generateAliasAsModel>
<generateSupportingFiles>false</generateSupportingFiles>
<configOptions>
<sourceFolder>src/gen/java/main</sourceFolder>
<interfaceOnly>true</interfaceOnly>
</configOptions>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.pierceecom.blog;

import java.io.IOException;

import javax.annotation.Priority;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.Priorities;

/**
* This is a response filter class to add CORS headers in the response.
* So that other applications (e.g. Angular application) can access resources from this web service resources.
*/

@Provider
@Priority(Priorities.HEADER_DECORATOR)
public class AccessControlResponseFilter implements ContainerResponseFilter {

@Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
final MultivaluedMap<String,Object> headers = responseContext.getHeaders();
headers.add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
headers.add("Access-Control-Allow-Origin", "*");
if (requestContext.getMethod().equalsIgnoreCase("OPTIONS")) {
headers.add("Access-Control-Allow-Headers", requestContext.getHeaderString("Access-Control-Request-Headers"));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

import com.pierceecom.blog.api.PostsApiImpl;


@ApplicationPath("/")
public class JAXRSConfiguration extends Application {
@Override
public Set<Class<?>> getClasses() {
HashSet<Class<?>> classes = new HashSet<>();
classes.add(AccessControlResponseFilter.class);
classes.add(PostsApiImpl.class);
classes.add(HelloPierceResource.class);
return classes;
}
Expand Down
91 changes: 91 additions & 0 deletions blog-web/src/main/java/com/pierceecom/blog/api/PostsApiImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package com.pierceecom.blog.api;

import java.util.Optional;
import java.util.concurrent.atomic.AtomicLong;

import javax.inject.Inject;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;

import com.pierceecom.blog.model.Post;
import com.pierceecom.blog.service.PostService;
import com.pierceecom.blog.swagger.PostsApi;

public class PostsApiImpl implements PostsApi{

private static final AtomicLong postSequenceId = new AtomicLong();
private static final String VALID_XML_CONTENT_REGEX = "[\\u0009\\u000a\\u000d\\u0020-\\uD7FF\\uE000-\\uFFFD]+$"; // regex to match valid XML Characters.
private static final int TITLE_MAX_LENGTH = 40;
private static final int CONTENT_MAX_LENGTH = 100;

private PostService postService;

@Inject
public PostsApiImpl(PostService postService) {
this.postService = postService;
}

@Override
public Response addPost(Post post, SecurityContext securityContext) {
if (!hasValidPostContent(post) || !isNullOrEmpty(post.getId())) {
return Response.status(Response.Status.BAD_REQUEST).build();
}
post.setId(Long.toString(postSequenceId.incrementAndGet()));
postService.addPost(post);
return Response.status(Response.Status.CREATED).entity(post).build();
}

@Override
public Response deletePost(String postId, SecurityContext securityContext) {
Optional<Post> optPost = postService.getPostById(postId);
if (optPost.isEmpty()) {
return Response.status(Response.Status.NOT_FOUND).build();
}
postService.deletePost(postId);
return Response.ok().build();
}

@Override
public Response getAllPosts(SecurityContext securityContext) {
return Response.ok(postService.getAllPosts()).build();
}

@Override
public Response getPostById(String postId, SecurityContext securityContext) {
Optional<Post> optPost = postService.getPostById(postId);
if (optPost.isEmpty()) {
return Response.status(Response.Status.NO_CONTENT).build();
}
return Response.ok(optPost.get()).build();
}

@Override
public Response updatePost(Post post, SecurityContext securityContext) {
if (isNullOrEmpty(post.getId()) || !hasValidPostContent(post)) {
return Response.status(Response.Status.BAD_REQUEST).build();
}
Optional<Post> optPost = postService.getPostById(post.getId());
if (optPost.isEmpty()) {
return Response.status(Response.Status.NOT_FOUND).build();
}
postService.updatePost(post);
return Response.status(Response.Status.CREATED).entity(post).build();
}

private boolean isNullOrEmpty(String s) {
if (s == null) {
return true;
}
return s.isBlank();
}

private boolean hasValidPostContent(Post post) {
if (!post.getTitle().matches(VALID_XML_CONTENT_REGEX) || !post.getContent().matches(VALID_XML_CONTENT_REGEX)) {
return false;
}
if (post.getTitle().length() > TITLE_MAX_LENGTH || post.getContent().length() > CONTENT_MAX_LENGTH) {
return false;
}
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.pierceecom.blog.service;

import java.util.Collection;
import java.util.Optional;

import com.pierceecom.blog.model.Post;

public interface PostService {

void addPost(Post post);

void deletePost(String postId);

Collection<Post> getAllPosts();

Optional<Post> getPostById(String postId);

void updatePost(Post post);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.pierceecom.blog.service;

import java.util.Collection;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import javax.enterprise.context.ApplicationScoped;

import com.pierceecom.blog.model.Post;

@ApplicationScoped
public class PostServiceImpl implements PostService {

private ConcurrentMap<String, Post> postMap = new ConcurrentHashMap<>();

protected PostServiceImpl() {
}

@Override
public void addPost(Post post) {
postMap.put(post.getId(), post);
}

@Override
public void deletePost(String postId) {
postMap.remove(postId);
}

@Override
public Collection<Post> getAllPosts() {
return postMap.values();
}

@Override
public Optional<Post> getPostById(String postId) {
return Optional.ofNullable(postMap.get(postId));
}

@Override
public void updatePost(Post post) {
postMap.put(post.getId(), post);
}

}
Loading