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
4 changes: 4 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
FROM openjdk:22
ARG JAR_FILE=out/artifacts/coding_project_jar/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
47 changes: 19 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,29 @@
# Harbor Take Home Project

Welcome to the Harbor take home project. We hope this is a good opportunity for you to showcase your skills.
## MVP Features supported:
- User is able to create, view, update, delete his profile
- User is able to create, view , modify and delete his availability
- User is able to share his entire availability to other users
- User is able to see all overlaps with any set of registered users
- user is able to create, view, update, cancel booking meeting

## The Challenge
## Test Coverage
- Added unit test for userService.(Skipped other classes in the interest of time)
- Exhaustive Integration Test Coverage using RestTemplate for all features except BookingController.
- Integration test coverage ensures working Apis and its correctness

Build us a REST API for calendly. Remember to support
## Assumptions
-Using InMemory Database for persisting data.
-All users register on the application to be able to use the features
- Have skipped some validation and incremental logic in some parts of application.Will keep on iterating on the same.

- Setting own availability
- Showing own availability
- Finding overlap in schedule between 2 users
## Future Improvements
- Add logging framework
- Emit appropriate metrics
- Integrate Swagger
- Integrate Java Cods Coverage

It is up to you what else to support.

## Expectations

We care about

- Have you thought through what a good MVP looks like? Does your API support that?
- What trade-offs are you making in your design?
- Working code - we should be able to pull and hit the code locally. Bonus points if deployed somewhere.
- Any good engineer will make hacks when necessary - what are your hacks and why?

We don't care about

- Authentication
- UI
- Perfection - good and working quickly is better

It is up to you how much time you want to spend on this project. There are likely diminishing returns as the time spent goes up.

## Submission

Please fork this repository and reach out to Prakash when finished.

## Next Steps

After submission, we will conduct a 30 to 60 minute code review in person. We will ask you about your thinking and design choices.
85 changes: 85 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.p0</groupId>
<artifactId>calendly</artifactId>
<version>0.0.2-SNAPSHOT</version>
<name>calendly</name>
<description>Demo project for Spring Boot</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>

<!-- https://mvnrepository.com/artifact/io.rest-assured/rest-assured -->
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>5.5.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-validation -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>3.0.2</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.6.0</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>
14 changes: 14 additions & 0 deletions src/main/java/org/p0/calendly/CalendlyApplication.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.p0.calendly;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class CalendlyApplication {

public static void main(String[] args) {
SpringApplication.run(CalendlyApplication.class, args);
System.out.println("Hello");
}

}
67 changes: 67 additions & 0 deletions src/main/java/org/p0/calendly/controllers/BookingController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package org.p0.calendly.controllers;

import lombok.NonNull;
import org.p0.calendly.dtos.*;
import org.p0.calendly.models.Booking;
import org.p0.calendly.services.BookingManagementService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/v1/book")
public class BookingController {


@Autowired
private BookingManagementService meetingManagementService;

// create booking
@PostMapping
public ResponseEntity<String> createBooking(@RequestBody @NonNull MeetingBookingRequest meetingBookingRequest) {
// todo: input validations
String response = meetingManagementService.createBooking(meetingBookingRequest);

return ResponseEntity.status(201).body(response);
}

// Get booking by bookingID
@GetMapping("/{id}")
public ResponseEntity<Booking> getBookingById(@PathVariable @NonNull String id) {
Booking response = null;
// todo: input validations
response= meetingManagementService.getBookingById(id);;

return ResponseEntity.status(200).body(response);
}

// Get booking by userId
@GetMapping("/user/{id}")
public ResponseEntity<List<Booking>> getBookingByuserId(@PathVariable @NonNull String id) {
List<Booking> response;

response= meetingManagementService.getBookingByuserId(id);

return ResponseEntity.status(200).body(response);
}

// Update booking by bookingId
@PutMapping("/{id}")
public ResponseEntity<Booking> updateBooking(@PathVariable @NonNull String id, @RequestBody @NonNull Booking bookingDetails) {
Booking response = null;

response= meetingManagementService.updateBooking(id, bookingDetails);


return ResponseEntity.ok(response);
}

// Delete booking by bookingId
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteBooking(@PathVariable String id) {
meetingManagementService.deleteBooking(id);
return ResponseEntity.noContent().build();
}
}
39 changes: 39 additions & 0 deletions src/main/java/org/p0/calendly/controllers/OverlapController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.p0.calendly.controllers;

import lombok.NonNull;
import org.p0.calendly.dtos.ScheduleOverlapRequest;
import org.p0.calendly.dtos.ScheduleOverlapResponse;
import org.p0.calendly.exceptions.NotEnoughUsersException;
import org.p0.calendly.services.AvailabilityOverlapService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.Arrays;
import java.util.List;

@RestController
@RequestMapping("/api/v1/overlap")
public class OverlapController {


@Autowired
private AvailabilityOverlapService availabilityOverlapService;

// get overlaps for comma separated userIds
@GetMapping("/ids={id}")
public ResponseEntity<ScheduleOverlapResponse> getOverlaps(@PathVariable @NonNull String id) {
// todo: validate input
List<String> scheduleOverlapRequest = Arrays.asList(id.split(",", -1));

ScheduleOverlapResponse response = new ScheduleOverlapResponse();
try {
response = availabilityOverlapService.getUserOverlaps(scheduleOverlapRequest);
}
catch (NotEnoughUsersException e)
{
return ResponseEntity.status(400).build();
}
return ResponseEntity.status(200).body(response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package org.p0.calendly.controllers;


import lombok.NonNull;
import org.p0.calendly.dtos.AvailabilityResponse;
import org.p0.calendly.dtos.Availabilityrequest;
import org.p0.calendly.exceptions.UserAlreadyExistsException;
import org.p0.calendly.exceptions.UserNotFoundException;
import org.p0.calendly.models.User;
import org.p0.calendly.services.UserAvailabilityService;
import org.p0.calendly.services.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/v1/availability")
public class UserAvailabilityController {


@Autowired
private UserAvailabilityService userAvailabilityService;

// Create user availability
@PostMapping
public ResponseEntity<Void> createUserAvailability(@RequestBody @NonNull Availabilityrequest availabilityrequest) {

User response = null;
try {
userAvailabilityService.createUserAvailabilty(availabilityrequest);
}
catch (UserNotFoundException e)
{
return ResponseEntity.status(404).build();
}
return ResponseEntity.status(201).build();
}

// Get user availability
@GetMapping("/{id}")
public ResponseEntity<AvailabilityResponse> getUserAvailability(@PathVariable @NonNull String id) {
AvailabilityResponse response = new AvailabilityResponse();
try {
response= userAvailabilityService.getUserAvailability(id);
}
catch (UserNotFoundException e)
{
return ResponseEntity.status(404).body(response);
}
return ResponseEntity.status(200).body(response);
}
}
Loading