Skip to content

Commit

Permalink
Merge pull request #2 from virtualidentityag/develop
Browse files Browse the repository at this point in the history
merge to staging
  • Loading branch information
tkuzynow authored Jun 5, 2024
2 parents 96cc57a + 08af73b commit d8e27a4
Show file tree
Hide file tree
Showing 10 changed files with 280 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.vi.appointmentservice.api.calcom.model.CalcomEventType;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import javax.sql.DataSource;
import javax.validation.constraints.NotNull;
Expand Down Expand Up @@ -39,6 +40,17 @@ public CalcomEventType getEventTypeById(Number eventTypeId) {
return getCalcomEventType(result);
}

public Optional<CalcomEventType> findEventTypeById(Number eventTypeId) {
String selectEvent = "SELECT * FROM \"EventType\" WHERE id = :eventTypeId";
SqlParameterSource parameters = new MapSqlParameterSource(EVENT_TYPE_ID, eventTypeId);
List<Map<String, Object>> result = db.queryForList(selectEvent, parameters);
if (result.isEmpty()) {
return Optional.empty();
} else {
return Optional.of(getCalcomEventType(result.get(0)));
}
}

public CalcomEventType getEventTypeByUserId(Number userId) {
String selectEvent = "SELECT * FROM \"EventType\" WHERE \"userId\" = :userId";
SqlParameterSource parameters = new MapSqlParameterSource(USER_ID, userId);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,31 @@
package com.vi.appointmentservice.api.calcom.repository;

import static org.openapitools.codegen.meta.features.DataTypeFeature.Maps;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.validation.constraints.NotNull;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.stereotype.Repository;

@Repository
@Slf4j
public class ScheduleRepository {

private final @NotNull JdbcTemplate jdbcTemplate;
Expand All @@ -29,12 +44,40 @@ public ScheduleRepository(@Qualifier("dbTemplate") NamedParameterJdbcTemplate db
this.jdbcTemplate = jdbcTemplate;
}

public List<Integer> deleteUserSchedules(Long calcomUserId) {
public Set<Integer> deleteUserSchedules(Long calcomUserId) {
var originalScheduleIds = getScheduleIdsByUserId(db, calcomUserId);
String DELETE_SCHEDULE = "DELETE FROM \"Schedule\" where \"userId\" = :userId";
SqlParameterSource parameters = new MapSqlParameterSource("userId", calcomUserId);
db.update(DELETE_SCHEDULE, parameters);
//TODO: return ids of removed schedules
return null;
var leftScheduleIds = getScheduleIdsByUserId(db, calcomUserId);
return Sets.difference(originalScheduleIds, leftScheduleIds);
}

public List<String> getTableNames(JdbcTemplate jdbcTemplate) throws SQLException {
List<String> tableNames = new ArrayList<>();
Connection connection = jdbcTemplate.getDataSource().getConnection();
DatabaseMetaData metaData = connection.getMetaData();
ResultSet rs = metaData.getTables(null, null, "%", null);
while (rs.next()) {
tableNames.add(rs.getString("TABLE_NAME"));
}
return tableNames;
}
public Set<Integer> getScheduleIdsByUserId(NamedParameterJdbcTemplate jdbcTemplate, Long userId) {
Set<Integer> scheduleIds = Sets.newHashSet();
Map<String, Object> params = new HashMap<>();
params.put("userId", userId);
String sql = "SELECT \"id\" FROM \"Schedule\" WHERE \"userId\" = :userId";
try {
List<Map<String, Object>> rows = jdbcTemplate.queryForList(sql, params);
for (Map<String, Object> row : rows) {
scheduleIds.add((Integer) row.get("id"));
}
} catch (DataAccessException e) {
log.error("Error while fetching schedule ids for user: {}", userId, e);
}

return scheduleIds;
}

public Long createDefaultSchedule(Long calcomUserId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.vi.appointmentservice.api.calcom.repository.MembershipsRepository;
import com.vi.appointmentservice.api.calcom.repository.WebhookRepository;
import com.vi.appointmentservice.api.facade.AppointmentType;
import com.vi.appointmentservice.api.facade.DefaultTextConstants;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
Expand Down Expand Up @@ -42,6 +43,10 @@ public CalcomEventType getEventTypeById(Number eventTypeId) {
return eventType;
}

public Optional<CalcomEventType> findEventTypeById(Number eventTypeId) {
return eventTypeRepository.findEventTypeById(eventTypeId);
}

public CalcomEventType getEventTypeByUserId(Number userId) {
return eventTypeRepository.getEventTypeByUserId(userId);
}
Expand Down Expand Up @@ -173,4 +178,14 @@ public void deleteEventType(Long eventTypeId) {
eventTypeRepository.removeTeamEventTypeMembershipsForEventType(eventTypeId);
eventTypeRepository.removeTeamEventHostsForEventType(eventTypeId);
}

public void updateEventTypeTitle(Long calComUserId, String displayName) {
CalcomEventType eventTypeByUserId = getEventTypeByUserId(calComUserId);
if (eventTypeByUserId.getTitle().contains(DefaultTextConstants.BERATUNG_MIT)) {
eventTypeByUserId.setTitle(DefaultTextConstants.BERATUNG_MIT_DEM_DER_BERATER_IN + " " + displayName);
eventTypeRepository.updateEventType(eventTypeByUserId);
} else {
log.warn("Skipping update of EventType because event type for the user {} does not contain text {}", calComUserId, DefaultTextConstants.BERATUNG_MIT_DEM_DER_BERATER_IN);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.vi.appointmentservice.api.facade;

import com.google.common.collect.Lists;
import com.vi.appointmentservice.api.calcom.model.CalcomEventType;
import com.vi.appointmentservice.api.calcom.model.CalcomUser;
import com.vi.appointmentservice.api.calcom.repository.AvailabilityRepository;
import com.vi.appointmentservice.api.calcom.repository.BookingRepository;
Expand All @@ -21,11 +23,11 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

Expand Down Expand Up @@ -76,8 +78,11 @@ public void patchAppointmentUser(String consultantId, PatchConsultantDTO consult
var name = getDisplayNameOrFallbackToFirstname(consultant);
Optional<CalcomUserToConsultant> userConsultant = userToConsultantRepository
.findByConsultantId(consultantId);
Long calComUserId = userConsultant.orElseThrow().getCalComUserId();
calComUserService
.updateUsername(userConsultant.orElseThrow().getCalComUserId(), name);
.updateUsername(calComUserId, name);

calComEventTypeService.updateEventTypeTitle(calComUserId, name);
}

private void linkConsultantToAppointmentUser(
Expand All @@ -90,7 +95,7 @@ private void linkConsultantToAppointmentUser(
void setupDefaultScheduleAndEventType(CalcomUser calcomUser) {
Long defaultScheduleId = scheduleRepository.createDefaultSchedule(calcomUser.getId());
AppointmentType defaultAppointmentType = appointmentService.createDefaultAppointmentType();
defaultAppointmentType.setTitle("Beratung mit dem / der Berater:in");
defaultAppointmentType.setTitle(DefaultTextConstants.BERATUNG_MIT_DEM_DER_BERATER_IN);
calComEventTypeService
.createEventType(calcomUser, defaultAppointmentType,
defaultScheduleId);
Expand All @@ -105,7 +110,7 @@ public void deleteConsultantHandler(String consultantId) {
// Delete personal event-types
calComEventTypeService.deleteAllEventTypesOfUser(calcomUserId);
// Delete schedules
List<Integer> deletedSchedules = scheduleRepository.deleteUserSchedules(calcomUserId);
Set<Integer> deletedSchedules = scheduleRepository.deleteUserSchedules(calcomUserId);
// Delete availabilities for schedules
for (Integer scheduleId : deletedSchedules) {
availabilityRepository.deleteAvailabilityByScheduleId(Long.valueOf(scheduleId));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.vi.appointmentservice.api.facade;

public class DefaultTextConstants {

private DefaultTextConstants() {
// private constructor to hide the implicit public one
}

public static final String BERATUNG_MIT = "Beratung mit ";
public static final String BERATUNG_MIT_DEM_DER_BERATER_IN = "Beratung mit dem / der Berater:in";
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,25 +38,38 @@ public CalcomBooking attachRescheduleLink(CalcomBooking calcomBooking) {
.getUserById(Long.valueOf(calcomBooking.getUserId()));
var teamId = getTeamIdForBooking(calcomBooking);
String slug = null;
if (teamId != null) {
CalcomTeam team = calComTeamService.getTeamById(teamId);
if (teamId.isPresent()) {
CalcomTeam team = calComTeamService.getTeamById(teamId.get());
slug = "team/" + team.getSlug();
} else {
slug = registeredCalcomUser.getUsername();
}

String eventTypeSlug = this.calcomEventTypeService.getEventTypeById(
Long.valueOf(calcomBooking.getEventTypeId())).getSlug();
calcomBooking.setRescheduleLink(
"/" + slug + "/" + eventTypeSlug + "?rescheduleUid=" + calcomBooking.getUid());
return attachRescheduleLinkIfEventTypeIsFound(calcomBooking, slug);
}

private CalcomBooking attachRescheduleLinkIfEventTypeIsFound(CalcomBooking calcomBooking, String slug) {
var optionalEventType = this.calcomEventTypeService.findEventTypeById(
Long.valueOf(calcomBooking.getEventTypeId()));

if (optionalEventType.isEmpty()) {
log.warn("EventType not found for bookingId " + calcomBooking.getId());
return calcomBooking;
} else {
calcomBooking.setRescheduleLink(
"/" + slug + "/" + optionalEventType.get().getSlug() + "?rescheduleUid=" + calcomBooking.getUid());
}
return calcomBooking;
}

private Number getTeamIdForBooking(CalcomBooking calcomBooking) {
CalcomEventType eventType = calcomEventTypeService
.getEventTypeById(Long.valueOf(calcomBooking.getEventTypeId()));
return eventType.getTeamId();
private Optional<Number> getTeamIdForBooking(CalcomBooking calcomBooking) {
Optional<CalcomEventType> eventType = calcomEventTypeService
.findEventTypeById(Long.valueOf(calcomBooking.getEventTypeId()));
if (eventType.isEmpty() || eventType.get().getTeamId() == null) {
return Optional.empty();
} else {
return Optional.of(eventType.get().getTeamId());
}
}

public void attachConsultantName(List<CalcomBooking> bookings) {
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/application-testing.properties
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ keycloak.config.app-client-id=app-ci
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
spring.sql.init.schema-locations=classpath*:database/AppointmentServiceDatabase.sql
calcom.database.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
calcom.database.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1;MODE=PostgreSQL
calcom.database.username=appointmentservice
calcom.database.password=appointmentservice
calcom.database.driverClass=org.h2.Driver
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.vi.appointmentservice.api.service.calcom;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;

import com.fasterxml.jackson.databind.ObjectMapper;
Expand All @@ -12,6 +13,7 @@
import org.assertj.core.util.Lists;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
Expand Down Expand Up @@ -63,4 +65,31 @@ public void shouldCreateEventTypeAndNotUpdateLocationsIfAppointmentTypeDoesNotCo
Mockito.verify(eventTypeRepository, Mockito.never()).updateLocations(Mockito.anyInt(), Mockito.anyString());
}

@Test
public void shouldUpdateEventTypeTitle() {
// given
var eventType = new CalcomEventType();
eventType.setId(1);
eventType.setTitle("Beratung mit dem / der Berater:in ConsultantFirstname");

Long calcomUserId = 2L;
when(eventTypeRepository.getEventTypeByUserId(calcomUserId)).thenReturn(eventType);

// when
calcomEventTypeService.updateEventTypeTitle(calcomUserId, "ConsultantDisplayName");
// then

ArgumentCaptor<CalcomEventType> captor = ArgumentCaptor.forClass(CalcomEventType.class);
Mockito.verify(eventTypeRepository).updateEventType(captor.capture());
assertThat(captor.getValue().getTitle()).isEqualTo("Beratung mit dem / der Berater:in ConsultantDisplayName");
}

@Test
public void shouldDelegateToRepositoryToFindEventType() {
// when
calcomEventTypeService.findEventTypeById(1L);
// then
Mockito.verify(eventTypeRepository).findEventTypeById(1L);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.vi.appointmentservice.api.calcom.repository;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.Set;
import org.junit.Before;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;

@TestPropertySource(properties = "spring.profiles.active=testing")
@AutoConfigureTestDatabase(replace = Replace.ANY)
@ExtendWith(SpringExtension.class)
@SpringBootTest
class ScheduleRepositoryTest {

@Autowired
ScheduleRepository scheduleRepository;

@Autowired
JdbcTemplate jdbcTemplate;

@Before
public void setUp() {
jdbcTemplate.execute("DROP TABLE IF EXISTS \"Schedule\"");
}
@Test
void deleteUserSchedules_Should_DeleteSchedulesPerUserId() {
// given
inititalizeDB();

jdbcTemplate.execute("INSERT INTO \"Schedule\" (\"id\", \"userId\", \"name\") VALUES (1, 1, 'DEFAULT_SCHEDULE')");
jdbcTemplate.execute("INSERT INTO \"Schedule\" (\"id\", \"userId\", \"name\") VALUES (2, 1, 'DEFAULT_SCHEDULE')");
// when
Set<Integer> integers = scheduleRepository.deleteUserSchedules(1L);
// then
assertThat(integers).containsOnly(1, 2);

}

private void inititalizeDB() {
// we can't use @Sql annotation here because it's not visible in jdbcTemplate,
// probably because there are defined multiple jdbc templates in this project for different datasources
jdbcTemplate.execute("create table \"Schedule\"\n"
+ "(\n"
+ " \"id\" integer not null\n"
+ " primary key,\n"
+ " \"userId\" integer not null,\n"
+ " \"name\" varchar(255) not null\n"
+ ");");
}

}
Loading

0 comments on commit d8e27a4

Please sign in to comment.