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

User should be able to start, record and end activity #18

Open
wants to merge 21 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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
5 changes: 5 additions & 0 deletions swift-activities/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,14 @@ repositories {
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation group: 'io.hypersistence', name: 'hypersistence-utils-hibernate-63', version: '3.7.1'
implementation 'com.fasterxml.jackson.module:jackson-module-jakarta-xmlbind-annotations'
implementation 'org.flywaydb:flyway-core'
runtimeOnly 'org.postgresql:postgresql'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.testcontainers:postgresql'
}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.nilenso.swiftactivities.controllers;

import com.nilenso.swiftactivities.models.dtos.GeolocationDto;
import com.nilenso.swiftactivities.services.ActivityService;
import jakarta.validation.Valid;
import com.nilenso.swiftactivities.models.Activity;
import com.nilenso.swiftactivities.models.dtos.StartActivityDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@RestController
@RequestMapping("/api/activity")
public class ActivityController {
private final ActivityService activityService;

public ActivityController(ActivityService activityService) {
this.activityService = activityService;
}

@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Map<String, UUID> createActivity(@Valid @RequestBody StartActivityDto startActivityDto) {
var activity = activityService.addActivity(startActivityDto);
Map<String, UUID> response = new HashMap<String, UUID>();
response.put("activityId", activity.getActivityId());
return response;
}

@PostMapping("/{activityId}/log")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void logGeolocationData(@PathVariable UUID activityId,
@Valid @RequestBody GeolocationDto geolocationDto) {
activityService.logGeolocationData(activityId, geolocationDto);
}

@PostMapping("/{activityId}/end")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void endActivity(@PathVariable UUID activityId) {
activityService.endActivity(activityId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package com.nilenso.swiftactivities.models;

import jakarta.persistence.*;

import java.time.Instant;
import java.util.Objects;
import java.util.UUID;

@Entity
@Table(name = "activity")
public class Activity {
public static enum ActivityType {
Running,
Swimming,
Cycling
}

@Id
@GeneratedValue(strategy = GenerationType.UUID)
@Column(name = "activity_id")
private UUID activityId;
@Column(name = "user_id")
private UUID userId;
@Column(name = "name")
private String name;
@Column(name = "type", columnDefinition = "activity_type")
@Enumerated(EnumType.STRING)
private ActivityType type;
@Column(name = "start_time")
private Instant startTime;
@Column(name = "end_time")
private Instant endTime;
@Column(name = "created_at")
private Instant createdAt;
@Column(name = "updated_at")
private Instant updatedAt;

public UUID getActivityId() {
return activityId;
}

public void setActivityId(UUID activityId) {
this.activityId = activityId;
}

public UUID getUserId() {
return userId;
}

public void setUserId(UUID userId) {
this.userId = userId;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public ActivityType getType() {
return type;
}

public void setType(ActivityType type) {
this.type = type;
}

public Instant getStartTime() {
return startTime;
}

public void setStartTime(Instant startTime) {
this.startTime = startTime;
}

public Instant getEndTime() {
return endTime;
}

public void setEndTime(Instant endTime) {
this.endTime = endTime;
}

public Instant getCreatedAt() {
return createdAt;
}

public void setCreatedAt(Instant createdAt) {
this.createdAt = createdAt;
}

public Instant getUpdatedAt() {
return updatedAt;
}

public void setUpdatedAt(Instant updatedAt) {
this.updatedAt = updatedAt;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Activity activity = (Activity) o;
return Objects.equals(activityId, activity.activityId)
&& Objects.equals(userId, activity.userId)
&& Objects.equals(name, activity.name)
&& type == activity.type
&& Objects.equals(startTime, activity.startTime)
&& Objects.equals(endTime, activity.endTime)
&& Objects.equals(createdAt, activity.createdAt)
&& Objects.equals(updatedAt, activity.updatedAt);
}

@Override
public int hashCode() {
return Objects.hash(activityId, userId, name, type, startTime, endTime, createdAt, updatedAt);
}

@Override
public String toString() {
return "Activity{" +
"activityId=" + activityId +
", userId=" + userId +
", name='" + name + '\'' +
", type=" + type +
", startTime=" + startTime +
", endTime=" + endTime +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt +
'}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package com.nilenso.swiftactivities.models;

import com.nilenso.swiftactivities.models.dtos.GeolocationDto;
import io.hypersistence.utils.hibernate.type.json.JsonType;
import jakarta.persistence.*;
import org.hibernate.annotations.Type;

import java.math.BigInteger;
import java.time.Instant;
import java.util.Objects;
import java.util.UUID;

@Entity
@Table(name = "activity_geolocation_data")
public class ActivityGeolocationData {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
@Column(name = "geolocation_data_id")
private UUID geolocationDataId;
@Column(name = "activity_id")
private UUID activityId;
@Type(JsonType.class)
@Column(name = "data")
private GeolocationDto data;
@Column(name = "recorded_at")
private Instant recordedAt;
@Column(name = "created_at")
private Instant createdAt;

public UUID getGeolocationDataId() {
return geolocationDataId;
}

public void setGeolocationDataId(UUID geolocationDataId) {
this.geolocationDataId = geolocationDataId;
}

public UUID getActivityId() {
return activityId;
}

public void setActivityId(UUID activityId) {
this.activityId = activityId;
}

public GeolocationDto getData() {
return data;
}

public void setData(GeolocationDto data) {
this.data = data;
}

public Instant getRecordedAt() {
return recordedAt;
}

public void setRecordedAt(Instant recordedAt) {
this.recordedAt = recordedAt;
}

public Instant getCreatedAt() {
return createdAt;
}

public void setCreatedAt(Instant createdAt) {
this.createdAt = createdAt;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ActivityGeolocationData that = (ActivityGeolocationData) o;
return Objects.equals(geolocationDataId, that.geolocationDataId) && Objects.equals(activityId, that.activityId) && Objects.equals(data, that.data) && Objects.equals(recordedAt, that.recordedAt) && Objects.equals(createdAt, that.createdAt);
}

@Override
public int hashCode() {
return Objects.hash(geolocationDataId, activityId, data, recordedAt, createdAt);
}

@Override
public String toString() {
return "ActivityGeolocationData{" +
"geolocationDataId=" + geolocationDataId +
", activityId=" + activityId +
", data=" + data +
", recordedAt=" + recordedAt +
", createdAt=" + createdAt +
'}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.nilenso.swiftactivities.models.dtos;

public record GeolocationDto(Double latitude,
Double longitude,
Double accuracy) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.nilenso.swiftactivities.models.dtos;

import com.nilenso.swiftactivities.models.Activity;
import jakarta.validation.constraints.NotNull;

public record StartActivityDto(String name,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't name be NotNull as well?

@NotNull(message = "Activity 'type' missing")
Activity.ActivityType type) { }
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.nilenso.swiftactivities.repositories;

import com.nilenso.swiftactivities.models.ActivityGeolocationData;
import org.springframework.data.jpa.repository.JpaRepository;

import java.math.BigInteger;
import java.util.UUID;

public interface ActivityGeolocationDataRepository extends JpaRepository<ActivityGeolocationData, UUID> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.nilenso.swiftactivities.repositories;

import com.nilenso.swiftactivities.models.Activity;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.UUID;

public interface ActivityRepository extends JpaRepository<Activity, UUID> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.nilenso.swiftactivities.services;

import com.nilenso.swiftactivities.models.Activity;
import com.nilenso.swiftactivities.models.dtos.GeolocationDto;
import com.nilenso.swiftactivities.models.dtos.StartActivityDto;

import java.util.UUID;

public interface ActivityService {
public Activity addActivity(StartActivityDto startActivityDto);

public void endActivity(UUID activityId);

public void logGeolocationData(UUID activityId, GeolocationDto geolocationDto);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.nilenso.swiftactivities.services;

import com.nilenso.swiftactivities.models.Activity;
import com.nilenso.swiftactivities.models.ActivityGeolocationData;
import com.nilenso.swiftactivities.models.dtos.GeolocationDto;
import com.nilenso.swiftactivities.models.dtos.StartActivityDto;
import com.nilenso.swiftactivities.repositories.ActivityGeolocationDataRepository;
import com.nilenso.swiftactivities.repositories.ActivityRepository;
import jakarta.persistence.EntityNotFoundException;
import org.springframework.stereotype.Service;

import java.time.Instant;
import java.util.UUID;

@Service
public class ActivityServiceImpl implements ActivityService {
private final ActivityRepository activityRepository;
private final ActivityGeolocationDataRepository geolocationDataRepository;

public ActivityServiceImpl(ActivityRepository activityRepository, ActivityGeolocationDataRepository geolocationDataRepository) {
this.activityRepository = activityRepository;
this.geolocationDataRepository = geolocationDataRepository;
}

@Override
public Activity addActivity(StartActivityDto startActivityDto) {
var activity = new Activity();
Instant currentUtcTime = Instant.now();
activity.setName(startActivityDto.name());
activity.setType(startActivityDto.type());
activity.setStartTime(currentUtcTime);
return activityRepository.save(activity);
}

@Override
public void endActivity(UUID activityId) {
Activity existingActivity = activityRepository.findById(activityId)
.orElseThrow(() -> new EntityNotFoundException("Couldn't find the activity"));
existingActivity.setEndTime(Instant.now());
activityRepository.save(existingActivity);
}

@Override
public void logGeolocationData(UUID activityId, GeolocationDto geolocationDto) {
var geolocationData = new ActivityGeolocationData();
geolocationData.setData(geolocationDto);
geolocationData.setActivityId(activityId);
geolocationDataRepository.save(geolocationData);
}
}
Loading