Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/integrating mongodb #1

Merged
merged 3 commits into from
Feb 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 20 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@


## Functionalities to be added
- [ ] MongoDB integration
- [x] MongoDB integration
- [ ] Unit tests
- [ ] Integration Tests
- [ ] CI/CD with Git actions
Expand All @@ -29,6 +29,7 @@ Project
| ├── api: contains controller and apis interfaces
| ├── config: contains all spring bean configuration
| ├── domain: contains all domain entity objects/classes
├── exception: contains all exception handlers
| ├── resource: contains property files and static content
├── kafka-consumer-example
| ├── Dockerfile
Expand All @@ -37,9 +38,11 @@ Project
| ├── java/com/project/kafkaexample
| ├── service: contains all of service implementation and business rules
| ├── dto: contains data transfer objects used in the integrations
| ├── api: contains controller and apis interfaces
| ├── respository: contains all interfaces with data-bases
| ├── config: contains all spring bean configuration
| ├── domain: contains all domain entity objects/classes
| ├── exception: contains all exception handlers
| ├── resource: contains property files and static content
├── docker-compose.yml

Expand All @@ -58,12 +61,26 @@ If you want to run it from scratch: 🔰
- wait until all containers are up and runing

### Testing:
Now it's time to user these examples by calling an api using curl or any other tools you prefer: 👽
Now it's time to use these examples by calling an api using curl or any other tools you prefer: 👽
```sh
curl -X POST 'http://localhost:8080/api/send' -d '{"name":"Full Name", "email": "email@email.com"}' -H 'content-type: application/json'
curl -X POST 'http://localhost:8080/api/send' -d '{"name":"Full Name", "email": "email@email.com"}' -H 'content-type: application/json'
```

### Validating:
To check if everything was ok, go to terminal and type following commands:
- `docker logs kafka-producer` to show kafka producer logs (here you should see a log saying you've sent a message) 🟢
- `docker logs kafka-consumer` to show kafka consumer logs (here you should see a log saying you've receved a new message) 🟢
- Now it's possible to query through API the sample message sent from producer to customer:
- query all people in data-base:
```sh
curl 'http://localhost:8081/api/people?number=0&size=10'
```
- query specific person using id: (remember replacing {id} for a valid id returned in the first query)
```sh
curl 'http://localhost:8081/api/people/{id}'
```

### Contact
[![Gmail Badge](https://img.shields.io/badge/-arturcampos13@gmail.com-c14438?style=flat&logo=Gmail&logoColor=white)](mailto:arturcampos13@gmail.com "Connect via Email")
[![Linkedin Badge](https://img.shields.io/badge/-arturcamposrodrigues-0072b1?style=flat&logo=Linkedin&logoColor=white)](https://www.linkedin.com/in/arturcamposrodrigues/?locale=en_US/ "Connect on LinkedIn")
[![Twitter Badge](https://img.shields.io/badge/-@_artur_campos-00acee?style=flat&logo=Twitter&logoColor=white)](https://twitter.com/intent/follow?screen_name=_artur_campos "Follow on Twitter")
13 changes: 13 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
version: "3"

services:

db:
image: mongo
container_name: mongo
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: M0rd0r!
ports:
- "27017:27017"
extra_hosts:
- "host.docker.internal:172.17.0.1"
volumes:
- "/tmp/.mongodb:/data/db"
producer:
build: ./kafka-producer-example
container_name: kafka-producer
Expand Down
3 changes: 3 additions & 0 deletions kafka-consumer-example/.idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 23 additions & 0 deletions kafka-consumer-example/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@
<artifactId>spring-boot-starter-actuator</artifactId>
<groupId>org.springframework.boot</groupId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<artifactId>spring-boot-starter-web</artifactId>
<groupId>org.springframework.boot</groupId>
Expand Down Expand Up @@ -56,6 +64,21 @@
<artifactId>spring-kafka-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>30.0-jre</version>
</dependency>
</dependencies>


Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package com.project.kafkaexample;

import com.project.kafkaexample.repository.PersonRepository;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;

@SpringBootApplication
public class KafkaExampleApplication {

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

public static void main(String[] args) {
SpringApplication.run(KafkaExampleApplication.class, args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.project.kafkaexample.api.Impl;

import com.project.kafkaexample.api.PersonResource;
import com.project.kafkaexample.converter.PersonMapper;
import com.project.kafkaexample.dto.PersonDTO;
import com.project.kafkaexample.service.PersonService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping
@RestController
@RequiredArgsConstructor
public class PersonResourceImpl implements PersonResource {

private final PersonService service;

@GetMapping("/people")
public ResponseEntity<Page<PersonDTO>> getAll(Pageable pageable) {
return ResponseEntity.ok(PersonMapper.getPersonPage(service.getAll(pageable)));
}

@GetMapping("/people/{id}")
public ResponseEntity<PersonDTO> getById(@PathVariable("id") String id) {
return ResponseEntity.ok(PersonMapper.fromDomain(service.getById(id)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.project.kafkaexample.api;

import com.project.kafkaexample.dto.PersonDTO;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;

public interface PersonResource {

ResponseEntity<Page<PersonDTO>> getAll(Pageable pageable);
ResponseEntity<PersonDTO> getById(@PathVariable("id") String id);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.project.kafkaexample.aspects;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Loggable {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.project.kafkaexample.aspects;

import java.util.Arrays;
import java.util.stream.Collectors;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LoggingAspect {

@Around("@annotation(loggable)")
public Object logMethodCall(ProceedingJoinPoint joinPoint, Loggable loggable) throws Throwable {
return handleJoinPoint(joinPoint);
}

@Around(
"execution(* com.project.kafkaexample..*Resource*.*(..)) || execution(* com.project.kafkaexample..*Repository*.*(..))")
public Object logMethodCall(ProceedingJoinPoint joinPoint) throws Throwable {
return handleJoinPoint(joinPoint);
}

private Object handleJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {
final Logger log = LoggerFactory.getLogger(joinPoint.getSignature().getDeclaringType());
final String methodName = joinPoint.getSignature().getName();
final Object[] args = joinPoint.getArgs();
final String argsStr =
Arrays.stream(args).map(String::valueOf).collect(Collectors.joining(",", "[", "]"));

log.info("[m=" + methodName + "] - Begin of " + methodName + " with arguments " + argsStr);

final Object result = joinPoint.proceed();

log.info("[m=" + methodName + "] - End of " + methodName + " resulting in " + result);

return result;
}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
package com.project.kafkaexample.config;

import java.util.HashMap;
import java.util.Map;
import org.apache.kafka.clients.admin.AdminClientConfig;
import org.apache.kafka.clients.admin.NewTopic;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.core.KafkaAdmin;

@Configuration
public class KafkaTopicConfig {

@Value(value = "${kafka.topic-name}")
private String topicName;
@Bean
public NewTopic sampleTopic() {
return new NewTopic(topicName, 3, (short) 1);
}
}
@Value(value = "${kafka.topic-name}")
private String topicName;

@Bean
public NewTopic sampleTopic() {
return new NewTopic(topicName, 3, (short) 1);
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,37 @@
package com.project.kafkaexample.converter;

import com.project.kafkaexample.dto.SampleRequest;
import com.project.kafkaexample.domain.Person;
import com.project.kafkaexample.dto.PersonDTO;
import com.project.kafkaexample.dto.PersonKafkaDTO;
import java.util.UUID;
import java.util.stream.Collectors;
import lombok.experimental.UtilityClass;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;

@UtilityClass
public class PersonMapper {
public static Person sourceToDestination(SampleRequest source){
return Person.builder()
.identifier(UUID.randomUUID().toString())
.name(source.getName())
.email(source.getEmail())
.build();
}

}
public Person toDomain(PersonKafkaDTO source) {
return Person.builder()
.id(UUID.randomUUID().toString())
.name(source.getName())
.email(source.getEmail())
.build();
}

public PersonDTO fromDomain(Person source) {
return PersonDTO.builder()
.id(source.getId())
.name(source.getName())
.email(source.getEmail())
.build();
}

public PageImpl<PersonDTO> getPersonPage(Page<Person> clientPage) {
return new PageImpl<>(
clientPage.getContent().stream().map(PersonMapper::fromDomain).collect(Collectors.toList()),
clientPage.getPageable(),
clientPage.getTotalElements());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,27 @@

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;

@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
@Getter
@Setter
@ToString
@Document(collection = "persons")
public class Person {

private String identifier;
@Id private String id;

private String name;

@Indexed(unique = true)
private String email;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.project.kafkaexample.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

@Builder
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@ToString
public class PersonDTO {

private String id;

private String name;

private String email;
}