diff --git a/service-webhook/pom.xml b/service-webhook/pom.xml new file mode 100644 index 0000000..d960bd5 --- /dev/null +++ b/service-webhook/pom.xml @@ -0,0 +1,123 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.3.3.RELEASE + + + fr.apside + service-webhook + 0.0.1-SNAPSHOT + service-webhook + Service de webhook au sein de la transition TOP + + + 11 + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-logging + + + + + + org.springframework.boot + spring-boot-starter-log4j2 + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + + + + org.projectlombok + lombok + 1.18.12 + provided + + + + + + org.springframework.cloud + spring-cloud-starter-openfeign + 2.2.4.RELEASE + + + io.github.openfeign + feign-okhttp + 9.3.1 + + + io.github.openfeign + feign-gson + 9.3.1 + + + io.github.openfeign + feign-slf4j + 9.3.1 + + + com.fasterxml.jackson.core + jackson-annotations + 2.11.2 + + + + + org.springframework.amqp + spring-rabbit-test + test + + + org.springframework.amqp + spring-amqp + 2.2.7.RELEASE + compile + + + org.springframework.amqp + spring-rabbit + 2.2.7.RELEASE + compile + + + + + com.fasterxml.jackson.core + jackson-databind + 2.11.2 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/service-webhook/src/main/java/fr/apside/servicewebhook/CandidateClient.java b/service-webhook/src/main/java/fr/apside/servicewebhook/CandidateClient.java new file mode 100644 index 0000000..c29987e --- /dev/null +++ b/service-webhook/src/main/java/fr/apside/servicewebhook/CandidateClient.java @@ -0,0 +1,18 @@ +package fr.apside.servicewebhook; + +import fr.apside.servicewebhook.model.CandidateResponse; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +import java.util.List; + +@FeignClient(name = "candidates", url = "${feign.url}") +public interface CandidateClient { + @RequestMapping(method = RequestMethod.GET, value = "/candidates") + List getCandidates(); + + @RequestMapping(method = RequestMethod.GET, value = "/candidates/{candidateId}", headers = "Authorization=Bearer ${bearer.token}") + CandidateResponse getCandidateById(@PathVariable("candidateId") Long candidateId); +} diff --git a/service-webhook/src/main/java/fr/apside/servicewebhook/ServiceWebhookApplication.java b/service-webhook/src/main/java/fr/apside/servicewebhook/ServiceWebhookApplication.java new file mode 100644 index 0000000..d40626b --- /dev/null +++ b/service-webhook/src/main/java/fr/apside/servicewebhook/ServiceWebhookApplication.java @@ -0,0 +1,40 @@ +package fr.apside.servicewebhook; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.amqp.AmqpException; +import org.springframework.amqp.AmqpRejectAndDontRequeueException; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +@SpringBootApplication +@EnableFeignClients +@RestController +public class ServiceWebhookApplication { + + public static Logger logger = LoggerFactory.getLogger(ServiceWebhookApplication.class); + + @Autowired + private RabbitTemplate template; + + @PostMapping + public void receipWebhook(@RequestBody long candidateId) { + logger.trace("Reception du nouveau collaborateur : " + candidateId); + try { + this.template.convertAndSend("WebhookWork", candidateId); + } catch (AmqpException ae) { + logger.error("Erreur lors de l'envois du message dans la queue de travail : "); + ae.printStackTrace(); + } + } + + public static void main(String[] args) { + SpringApplication.run(ServiceWebhookApplication.class, args); + } +} \ No newline at end of file diff --git a/service-webhook/src/main/java/fr/apside/servicewebhook/Worker.java b/service-webhook/src/main/java/fr/apside/servicewebhook/Worker.java new file mode 100644 index 0000000..63a4edc --- /dev/null +++ b/service-webhook/src/main/java/fr/apside/servicewebhook/Worker.java @@ -0,0 +1,63 @@ +package fr.apside.servicewebhook; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import feign.FeignException; +import fr.apside.servicewebhook.model.Candidate; +import org.slf4j.Logger; +import org.springframework.amqp.AmqpException; +import org.springframework.amqp.AmqpRejectAndDontRequeueException; +import org.springframework.amqp.core.Message; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.amqp.utils.SerializationUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.UUID; + +@Component +public class Worker { + + Logger logger = ServiceWebhookApplication.logger; + + @Autowired + private RabbitTemplate template; + + @Autowired + private CandidateClient client; + + @RabbitListener(queues = "WebhookWork") + public void receiveMessage(Message message) throws AmqpRejectAndDontRequeueException{ + receive(message, 1); + } + + public void receive(Message in, int receiver) throws AmqpRejectAndDontRequeueException { + long idCandidate = Long.parseLong(SerializationUtils.deserialize(in.getBody()).toString()); //conversion du body de byte[] a long + ObjectMapper objectMapper = new ObjectMapper(); + try{ + logger.trace("Recuperation des donnees du nouveau collaborateur : "); + Candidate newCandidate = client.getCandidateById(idCandidate).getCandidate(); + newCandidate.setFields2(logger); // parse les fields dans les attributs de Candidate et set fields a null (pour le parsing) + + newCandidate.setId(UUID.randomUUID().toString()); // Genere un UUID et le set au nouveau collaborateur + + String newCandidateAsSJson = objectMapper.writeValueAsString(newCandidate); + + logger.trace("Envois des donnees via RabbitMQ"); + this.template.convertAndSend("WebhookExchange", "collaborateur.new", newCandidateAsSJson); + }catch (FeignException fe){ + logger.error("Erreur lors de la recuperation des donnees : "); + fe.printStackTrace(); + throw new AmqpRejectAndDontRequeueException("Erreur lors de la recuperation des donnees"); + }catch (AmqpException ae){ + logger.error("Erreur lors de l'envois des donnees : "); + ae.printStackTrace(); + throw new AmqpRejectAndDontRequeueException("Erreur lors de l'envois des donnees"); + }catch (JsonProcessingException je){ + logger.error("Erreur lors du parsing des donnees au format Json : "); + je.printStackTrace(); + throw new AmqpRejectAndDontRequeueException("Erreur lors du parsing des donnees au format Json"); + } + } +} \ No newline at end of file diff --git a/service-webhook/src/main/java/fr/apside/servicewebhook/model/Candidate.java b/service-webhook/src/main/java/fr/apside/servicewebhook/model/Candidate.java new file mode 100644 index 0000000..f085a69 --- /dev/null +++ b/service-webhook/src/main/java/fr/apside/servicewebhook/model/Candidate.java @@ -0,0 +1,148 @@ +package fr.apside.servicewebhook.model; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import org.slf4j.Logger; + +import java.util.ArrayList; +import java.util.List; + +@Getter +@Setter +@ToString +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Candidate { + @JsonProperty("id") + private String id; + + @JsonProperty("fields") + private List fields = null; + + @JsonProperty("posteRecherche") + private List posteRecherche = null; + @JsonProperty("motifRecherche") + private List motifRecherche = null; + @JsonProperty("pistes") + private List pistes = null; + @JsonProperty("criteresChoix") + private List criteresChoix = null; + @JsonProperty("disponibilite") + private String disponibilite; + @JsonProperty("mobilitee") + private List mobilitee = null; + @JsonProperty("pretentionSalariale") + private List pretentionSalariale = null; + @JsonProperty("skills") + private List skills = null; + @JsonProperty("languageSkills") + private List languageSkills = null; + @JsonProperty("rqth") + private boolean rqth; + @JsonProperty("genre") + private String genre; + @JsonProperty("nationalite") + private List nationalite = null; + @JsonProperty("adresses") + private List adresses = null; + @JsonProperty("references") + private List references = null; + @JsonProperty("education") + private List education = null; + @JsonProperty("experience") + private List experience = null; + + @JsonProperty("tags") + private List tags = null; + @JsonProperty("phones") + private List phones = null; + @JsonProperty("name") + private String name; + @JsonProperty("photo_url") + private String photoUrl; + @JsonProperty("emails") + private List emails = null; + + public void setFields2(Logger log){ + for (Field f : this.fields){ + if (f.getName() == null && f.getValues() != null){ + switch (f.getKind()){ + case "skills": + this.skills = f.getValues(); + break; + case "language_skill": + this.languageSkills = f.getValues(); + break; + case "gender": + String genderString = f.getValues().toString(); + this.genre = genderString.substring(genderString.indexOf("=") + 1, genderString.length() - 2); + break; + case "nationality": + this.nationalite = f.getValues(); + break; + case "address": + this.adresses = new ArrayList<>(); + for(Object v : f.getValues()){ + String adString = v.toString(); + this.adresses.add(adString.substring(adString.indexOf("=") + 1, adString.length() - 2)); + } + break; + case "education": + this.education = f.getValues(); + break; + case "experience": + this.experience = f.getValues(); + break; + default: + log.warn("Champ inconnu dans le collaborateur : " + f.getKind()); + break; + } + }else if(f.getValues() != null){ + switch (f.getName()){ + case "Poste et contrat recherché": + this.posteRecherche = new ArrayList<>(); + for(Object v : f.getValues()) { + String posteString = v.toString(); + this.posteRecherche.add(posteString.substring(posteString.indexOf("=") + 1, posteString.length() - 2)); + } + break; + case "Motif recherche": + this.motifRecherche = new ArrayList<>(); + for(Object v : f.getValues()) { + String motifString = v.toString(); + this.motifRecherche.add(motifString.substring(motifString.indexOf("=") + 1, motifString.length() - 2)); + } + case "Pistes": + this.pistes = f.getValues(); + break; + case "Critères de choix": + this.criteresChoix = f.getValues(); + break; + case "Disponibilité": + String dispoString = f.getValues().get(0).toString(); + this.disponibilite = dispoString.substring(dispoString.indexOf("=") + 1, dispoString.length() - 2); + break; + case "Mobilité": + this.mobilitee = f.getValues(); + break; + case "Prétentions salariales": + this.pretentionSalariale = f.getValues(); + break; + case "RQTH": + String rqthString = f.getValues().get(0).toString(); + this.rqth = Boolean.parseBoolean(rqthString.substring(rqthString.indexOf("=") + 1, rqthString.length() - 2)); + break; + case "Références": + this.references = f.getValues(); + break; + default: + log.warn("Champ inconnu dans le collaborateur : " + f.getName()); + break; + } + } + } + this.fields = null; + } +} diff --git a/service-webhook/src/main/java/fr/apside/servicewebhook/model/CandidateResponse.java b/service-webhook/src/main/java/fr/apside/servicewebhook/model/CandidateResponse.java new file mode 100644 index 0000000..b5a8590 --- /dev/null +++ b/service-webhook/src/main/java/fr/apside/servicewebhook/model/CandidateResponse.java @@ -0,0 +1,21 @@ +package fr.apside.servicewebhook.model; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({ + "candidate" +}) +@Getter +@Setter +@ToString +public class CandidateResponse { + @JsonProperty("candidate") + private Candidate candidate; + +} diff --git a/service-webhook/src/main/java/fr/apside/servicewebhook/model/Field.java b/service-webhook/src/main/java/fr/apside/servicewebhook/model/Field.java new file mode 100644 index 0000000..dc9f364 --- /dev/null +++ b/service-webhook/src/main/java/fr/apside/servicewebhook/model/Field.java @@ -0,0 +1,20 @@ +package fr.apside.servicewebhook.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +import java.util.List; + +@Getter +@Setter +@ToString +public class Field { + @JsonProperty("kind") + private String kind; + @JsonProperty("name") + private String name; + @JsonProperty("values") + private List values = null; +} diff --git a/service-webhook/src/main/resources/application.properties b/service-webhook/src/main/resources/application.properties new file mode 100644 index 0000000..d573911 --- /dev/null +++ b/service-webhook/src/main/resources/application.properties @@ -0,0 +1,4 @@ +#API Recruitee +bearer.token=QkU4LzlyTnFlektjajhrR0JHbFNQUT09 +feign.url=https://api.recruitee.com/c/${id.apside} +id.apside=41694 \ No newline at end of file diff --git a/service-webhook/src/main/resources/log4j2-spring.xml b/service-webhook/src/main/resources/log4j2-spring.xml new file mode 100644 index 0000000..a17734a --- /dev/null +++ b/service-webhook/src/main/resources/log4j2-spring.xml @@ -0,0 +1,35 @@ + + + + + + + + + + %d %p %C{1.} [%t] %m%n + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/service-webhook/src/test/java/fr/apside/servicewebhook/ServiceWebhookApplicationTests.java b/service-webhook/src/test/java/fr/apside/servicewebhook/ServiceWebhookApplicationTests.java new file mode 100644 index 0000000..aa6abb0 --- /dev/null +++ b/service-webhook/src/test/java/fr/apside/servicewebhook/ServiceWebhookApplicationTests.java @@ -0,0 +1,13 @@ +package fr.apside.servicewebhook; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class ServiceWebhookApplicationTests { + + @Test + void contextLoads() { + } + +}