Skip to content
Closed
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
28 changes: 28 additions & 0 deletions web-backend/src/main/java/com/statusneo/vms/model/EmployeeDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.statusneo.vms.model;

public class EmployeeDTO {

private String displayName;
private String mail;

public String getDisplayName() {
return displayName;
}

public void setDisplayName(String displayName) {
this.displayName = displayName;
}

public String getMail() {
return mail;
}

public void setMail(String mail) {
this.mail = mail;
}

public EmployeeDTO(String displayName, String mail) {
this.displayName = displayName;
this.mail = mail;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,6 @@
@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
List<Employee> findByNameStartingWithIgnoreCase(String name);

boolean existsByEmail(String mail);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.statusneo.vms.service;

import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
import org.springframework.stereotype.Service;

@Service
public class AccessTokenService {

private final OAuth2AuthorizedClientManager authorizedClientManager;

public AccessTokenService(OAuth2AuthorizedClientManager authorizedClientManager) {
this.authorizedClientManager = authorizedClientManager;
}

public String getAccessToken() {
OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest
.withClientRegistrationId("azure")
.principal("principal") // client_credentials flow
.build();

OAuth2AuthorizedClient authorizedClient = authorizedClientManager.authorize(authorizeRequest);

if (authorizedClient != null && authorizedClient.getAccessToken() != null) {
return authorizedClient.getAccessToken().getTokenValue();
}

throw new RuntimeException("Failed to obtain access token");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package com.statusneo.vms.service;

import com.statusneo.vms.model.Employee;
import com.statusneo.vms.model.EmployeeDTO;
import com.statusneo.vms.repository.EmployeeRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -30,9 +31,11 @@
public class EmployeeService {
private static final Logger logger = LoggerFactory.getLogger(EmployeeService.class);
private final EmployeeRepository employeeRepository;
private final GraphApiService graphApiService;

public EmployeeService(EmployeeRepository employeeRepository) {
public EmployeeService(EmployeeRepository employeeRepository, GraphApiService graphApiService) {
this.employeeRepository = employeeRepository;
this.graphApiService = graphApiService;
}

public List<Employee> searchEmployeesByName(String prefix) {
Expand All @@ -41,4 +44,17 @@ public List<Employee> searchEmployeesByName(String prefix) {
logger.info("Found {} employees matching prefix '{}'", employees.size(), prefix);
return employees;
}

public void syncEmployees() {
List<EmployeeDTO> employees = graphApiService.getEmployees();

for (EmployeeDTO dto : employees) {
if (dto.getMail() != null && !employeeRepository.existsByEmail(dto.getMail())) {
Employee employee = new Employee();
employee.setName(dto.getDisplayName());
employee.setEmail(dto.getMail());
employeeRepository.save(employee);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.statusneo.vms.service;

import com.fasterxml.jackson.databind.JsonNode;
import com.statusneo.vms.model.EmployeeDTO;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.ArrayList;
import java.util.List;

@Service
public class GraphApiService {

@Value("${graph.api.url}")
private String graphApiUrl;

private final RestTemplate restTemplate;
private final OAuth2AuthorizedClientManager authorizedClientManager;
private final AccessTokenService accessTokenService;


public GraphApiService(RestTemplate restTemplate, OAuth2AuthorizedClientManager authorizedClientManager, AccessTokenService accessTokenService) {
this.restTemplate = restTemplate;
this.authorizedClientManager = authorizedClientManager;
this.accessTokenService = accessTokenService;
}


public List<EmployeeDTO> getEmployees() {
HttpHeaders headers = new HttpHeaders();
String accessToken = accessTokenService.getAccessToken();

headers.setBearerAuth(accessToken);
headers.setAccept(List.of(MediaType.APPLICATION_JSON));

HttpEntity<Void> request = new HttpEntity<>(headers);
ResponseEntity<JsonNode> response = restTemplate.exchange(
graphApiUrl + "/users", HttpMethod.GET, request, JsonNode.class);

List<EmployeeDTO> employees = new ArrayList<>();
if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
for (JsonNode user : response.getBody().get("value")) {
String displayName = user.hasNonNull("displayName") ? user.get("displayName").asText() : "N/A";
String mail = user.hasNonNull("mail") ? user.get("mail").asText() : "N/A";
employees.add(new EmployeeDTO(displayName, mail));
}
}

return employees;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,21 @@ public class ScheduledTasks {

private final ExcelService excelService;

public ScheduledTasks(ExcelService excelService) {
private final EmployeeService employeeService;

public ScheduledTasks(ExcelService excelService, EmployeeService employeeService) {
this.excelService = excelService;
this.employeeService = employeeService;
}

@Scheduled(fixedRateString = "${vms.scheduled.report.rate:43200000}") // Runs every 12 hours by default
public void sendVisitorReport() {
excelService.sendVisitorReport();
}

// Runs every 24 hours (default if not configured)
@Scheduled(fixedRateString = "${vms.scheduled.employee.sync.rate:24h}")
public void runDailySync() {
employeeService.syncEmployees();
}
}
4 changes: 4 additions & 0 deletions web-backend/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,7 @@ vms:

mail:
url: http://localhost:${wiremock.server.port}/v1.0/users/%s/sendMail

graph:
api:
url: http://localhost:${wiremock.server.port}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package com.statusneo.vms.service;

import com.statusneo.vms.TestcontainersConfiguration;
import com.statusneo.vms.model.Employee;
import com.statusneo.vms.model.EmployeeDTO;
import com.statusneo.vms.repository.EmployeeRepository;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;

import java.util.List;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
@SpringBootTest
@Import({TestcontainersConfiguration.class})
class EmployeeServiceTest {

@Mock
private EmployeeRepository employeeRepository;

@Mock
private GraphApiService graphApiService;

@InjectMocks
private EmployeeService employeeService;

@Test
public void shouldSaveNewEmployees() {
// Arrange
List<EmployeeDTO> employeeDTOs = List.of(
new EmployeeDTO("Amit Jangra", "amit@example.com"),
new EmployeeDTO("John Doe", "john@example.com")
);

when(graphApiService.getEmployees()).thenReturn(employeeDTOs);
when(employeeRepository.existsByEmail("amit@example.com")).thenReturn(false);
when(employeeRepository.existsByEmail("john@example.com")).thenReturn(false);

// Act
employeeService.syncEmployees();

// Assert
ArgumentCaptor<Employee> captor = ArgumentCaptor.forClass(Employee.class);
verify(employeeRepository, times(2)).save(captor.capture());

List<Employee> savedEmployees = captor.getAllValues();

assertEquals("Amit Jangra", savedEmployees.get(0).getName());
assertEquals("amit@example.com", savedEmployees.get(0).getEmail());

assertEquals("John Doe", savedEmployees.get(1).getName());
assertEquals("john@example.com", savedEmployees.get(1).getEmail());
}

@Test
public void shouldNotSaveIfEmailExistsOrNull() {
// Arrange
List<EmployeeDTO> employeeDTOs = List.of(
new EmployeeDTO("Amit Jangra", "amit@example.com"),
new EmployeeDTO("Anonymous", null), // invalid (null email)
new EmployeeDTO("John Doe", "john@example.com")
);

when(graphApiService.getEmployees()).thenReturn(employeeDTOs);
when(employeeRepository.existsByEmail("amit@example.com")).thenReturn(true); // already exists
when(employeeRepository.existsByEmail("john@example.com")).thenReturn(false);

// Act
employeeService.syncEmployees();

// Assert
verify(employeeRepository, times(1)).save(any());
verify(employeeRepository).save(argThat(emp ->
emp.getName().equals("John Doe") && emp.getEmail().equals("john@example.com")));
}
}
Loading