diff --git a/.github/workflows/RELEASE-docker-build-and-push.yml b/.github/workflows/RELEASE-docker-build-and-push.yml
new file mode 100644
index 0000000..2262705
--- /dev/null
+++ b/.github/workflows/RELEASE-docker-build-and-push.yml
@@ -0,0 +1,57 @@
+name: RELEASE - Build and push Docker official image to Github packages
+on:
+ push:
+ branches:
+ - main
+ paths-ignore:
+ - "**/README.md"
+ - ".github/workflows/**"
+ - "**/.gitignore"
+ workflow_dispatch:
+
+env:
+ IMAGE_NAME: ghcr.io/${{ github.repository }}/sympoll-group-service
+
+jobs:
+ build-and-push:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v2
+
+ - name: Set up QEMU
+ uses: docker/setup-qemu-action@v2
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v2
+
+ - name: Log in to GitHub Packages
+ run: echo "${{ secrets.PACKAGE_TOKEN }}" | docker login ghcr.io -u ${{ github.repository_owner }} --password-stdin
+
+ - name: Get current date
+ id: date
+ run: echo "::set-output name=DATE::$(date +'%d.%m.%Y')"
+
+ - name: Cache Docker layers
+ uses: actions/cache@v3
+ with:
+ path: ~/.docker/builder
+ key: ${{ runner.os }}-build-${{ github.sha }} # Cache key based on OS and commit SHA
+ restore-keys: | # Check for existing cache based on previous builds with same OS and SHA
+ ${{ runner.os }}-build-
+
+ - name: Build and push Docker image
+ id: docker_build
+ uses: docker/build-push-action@v2
+ with:
+ context: .
+ file: Dockerfile
+ push: true
+ platforms: linux/amd64,linux/arm64
+ tags: |
+ ${{ env.IMAGE_NAME }}:${{ steps.date.outputs.DATE }}
+ ${{ env.IMAGE_NAME }}:latest
+ cache-from: type=gha,key=${{ runner.os }}-build-${{ github.sha }} # Use cached layers based on commit SHA
+ cache-to: type=gha,mode=max # Enable caching to GitHub Actions
+
diff --git a/.github/workflows/TEST_CONTAINER-docker-build-and-push.yml b/.github/workflows/TEST_CONTAINER-docker-build-and-push.yml
new file mode 100644
index 0000000..0a618dc
--- /dev/null
+++ b/.github/workflows/TEST_CONTAINER-docker-build-and-push.yml
@@ -0,0 +1,49 @@
+name: TEST CONTAINER - Build and push Docker test image to Github packages
+on:
+ workflow_dispatch:
+
+env:
+ IMAGE_NAME: ghcr.io/${{ github.repository }}/sympoll-group-service-test
+
+jobs:
+ build-and-push:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v2
+
+ - name: Set up QEMU
+ uses: docker/setup-qemu-action@v2
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v2
+
+ - name: Log in to GitHub Packages
+ run: echo "${{ secrets.PACKAGE_TOKEN }}" | docker login ghcr.io -u ${{ github.repository_owner }} --password-stdin
+
+ - name: Get current date
+ id: date
+ run: echo "::set-output name=DATE::$(date +'%d.%m.%Y')"
+
+ - name: Cache Docker layers
+ uses: actions/cache@v3
+ with:
+ path: ~/.docker/builder
+ key: ${{ runner.os }}-build-${{ github.sha }} # Cache key based on OS and commit SHA
+ restore-keys: | # Check for existing cache based on previous builds with same OS and SHA
+ ${{ runner.os }}-build-
+
+ - name: Build and push Docker image
+ id: docker_build
+ uses: docker/build-push-action@v2
+ with:
+ context: .
+ file: Dockerfile
+ push: true
+ platforms: linux/amd64,linux/arm64
+ tags: |
+ ${{ env.IMAGE_NAME }}:${{ steps.date.outputs.DATE }}
+ ${{ env.IMAGE_NAME }}:latest
+ cache-from: type=gha,key=${{ runner.os }}-build-${{ github.sha }} # Use cached layers based on commit SHA
+ cache-to: type=gha,mode=max # Enable caching to GitHub Actions
diff --git a/docker/init.sql b/docker/init.sql
new file mode 100644
index 0000000..9c6ef41
--- /dev/null
+++ b/docker/init.sql
@@ -0,0 +1,16 @@
+-- Group Service Schema
+CREATE TABLE groups
+(
+ group_id VARCHAR(255) PRIMARY KEY,
+ group_name VARCHAR(255),
+ description TEXT,
+ creator_id UUID,
+ time_created TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+
+CREATE TABLE members
+(
+ user_id uuid NOT NULL,
+ group_id VARCHAR(255) REFERENCES groups(group_id) ON DELETE CASCADE NOT NULL,
+ PRIMARY KEY (group_id, user_id)
+);
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index dc32c63..d5713ff 100644
--- a/pom.xml
+++ b/pom.xml
@@ -39,6 +39,13 @@
spring-boot-starter-data-jpa
3.2.4
+
+
+ org.projectlombok
+ lombok
+ 1.18.34
+ provided
+
diff --git a/src/main/java/com/MTAPizza/Sympoll/groupmanagementservice/controller/ServiceController.java b/src/main/java/com/MTAPizza/Sympoll/groupmanagementservice/controller/ServiceController.java
new file mode 100644
index 0000000..c974828
--- /dev/null
+++ b/src/main/java/com/MTAPizza/Sympoll/groupmanagementservice/controller/ServiceController.java
@@ -0,0 +1,26 @@
+package com.MTAPizza.Sympoll.groupmanagementservice.controller;
+
+import com.MTAPizza.Sympoll.groupmanagementservice.dto.request.GroupCreateRequest;
+import com.MTAPizza.Sympoll.groupmanagementservice.dto.response.GroupResponse;
+import com.MTAPizza.Sympoll.groupmanagementservice.service.GroupService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.*;
+
+@Slf4j
+@RestController
+@RequestMapping("api/group")
+@RequiredArgsConstructor
+public class ServiceController {
+ private final GroupService groupService;
+
+ @PostMapping
+ @ResponseStatus(HttpStatus.CREATED)
+ public GroupResponse createGroup(@RequestBody GroupCreateRequest groupCreateRequest) {
+ log.info("Received request to create a group");
+ log.debug("Group to create received: {}", groupCreateRequest);
+ return groupService.createGroup(groupCreateRequest);
+ }
+
+}
diff --git a/src/main/java/com/MTAPizza/Sympoll/groupmanagementservice/dto/request/GroupCreateRequest.java b/src/main/java/com/MTAPizza/Sympoll/groupmanagementservice/dto/request/GroupCreateRequest.java
new file mode 100644
index 0000000..aae7054
--- /dev/null
+++ b/src/main/java/com/MTAPizza/Sympoll/groupmanagementservice/dto/request/GroupCreateRequest.java
@@ -0,0 +1,10 @@
+package com.MTAPizza.Sympoll.groupmanagementservice.dto.request;
+
+import java.util.UUID;
+
+public record GroupCreateRequest(
+ String groupId, // can be null, if so then generate a group number
+ String groupName,
+ String description,
+ UUID creatorId
+) {}
\ No newline at end of file
diff --git a/src/main/java/com/MTAPizza/Sympoll/groupmanagementservice/dto/response/GroupResponse.java b/src/main/java/com/MTAPizza/Sympoll/groupmanagementservice/dto/response/GroupResponse.java
new file mode 100644
index 0000000..9503e0a
--- /dev/null
+++ b/src/main/java/com/MTAPizza/Sympoll/groupmanagementservice/dto/response/GroupResponse.java
@@ -0,0 +1,14 @@
+package com.MTAPizza.Sympoll.groupmanagementservice.dto.response;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.UUID;
+
+public record GroupResponse (
+ String groupId,
+ String groupName,
+ String description,
+ UUID creatorId,
+ LocalDateTime timeCreated,
+ List membersList
+) {}
diff --git a/src/main/java/com/MTAPizza/Sympoll/groupmanagementservice/dto/response/MemberResponse.java b/src/main/java/com/MTAPizza/Sympoll/groupmanagementservice/dto/response/MemberResponse.java
new file mode 100644
index 0000000..2afe8c3
--- /dev/null
+++ b/src/main/java/com/MTAPizza/Sympoll/groupmanagementservice/dto/response/MemberResponse.java
@@ -0,0 +1,7 @@
+package com.MTAPizza.Sympoll.groupmanagementservice.dto.response;
+
+import java.util.UUID;
+
+public record MemberResponse(
+ UUID userId
+) {}
diff --git a/src/main/java/com/MTAPizza/Sympoll/groupmanagementservice/model/Group.java b/src/main/java/com/MTAPizza/Sympoll/groupmanagementservice/model/Group.java
new file mode 100644
index 0000000..3ad852e
--- /dev/null
+++ b/src/main/java/com/MTAPizza/Sympoll/groupmanagementservice/model/Group.java
@@ -0,0 +1,54 @@
+package com.MTAPizza.Sympoll.groupmanagementservice.model;
+
+import com.MTAPizza.Sympoll.groupmanagementservice.dto.response.GroupResponse;
+import jakarta.persistence.*;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+@Entity
+@Table(name = "groups")
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class Group {
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ private String groupId;
+
+ @Column(name = "group_name")
+ private String groupName;
+
+ @Column(name = "description")
+ private String description;
+
+ @Column(name = "created_by_user")
+ private UUID creatorId;
+
+ @Column(name = "time_created")
+ private final LocalDateTime timeCreated = LocalDateTime.now(); // Initialize to the current time.
+
+ @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
+ @JoinColumn(name = "group_id")
+ private List membersList = new ArrayList<>(); // Initialize to an empty members list.
+
+ // TODO: Add Admins list, will be initialized with the creatorId as the only admin.
+
+ public GroupResponse toGroupResponse() {
+ return new GroupResponse(
+ groupId,
+ groupName,
+ description,
+ creatorId,
+ timeCreated,
+ membersList.stream().map(Member::toMemberResponse).toList() // Convert to member response
+ );
+ }
+}
diff --git a/src/main/java/com/MTAPizza/Sympoll/groupmanagementservice/model/Member.java b/src/main/java/com/MTAPizza/Sympoll/groupmanagementservice/model/Member.java
new file mode 100644
index 0000000..0cdbe01
--- /dev/null
+++ b/src/main/java/com/MTAPizza/Sympoll/groupmanagementservice/model/Member.java
@@ -0,0 +1,27 @@
+package com.MTAPizza.Sympoll.groupmanagementservice.model;
+
+import com.MTAPizza.Sympoll.groupmanagementservice.dto.response.MemberResponse;
+import jakarta.persistence.*;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.UUID;
+
+@Entity
+@Table(name = "members")
+@NoArgsConstructor
+@Data
+public class Member {
+ @Column(name = "group_id")
+ private String groupId;
+
+ @Id
+ @Column(name = "user_id")
+ private UUID userId;
+
+ public MemberResponse toMemberResponse() {
+ return new MemberResponse(
+ userId
+ );
+ }
+}
diff --git a/src/main/java/com/MTAPizza/Sympoll/groupmanagementservice/repository/GroupRepository.java b/src/main/java/com/MTAPizza/Sympoll/groupmanagementservice/repository/GroupRepository.java
new file mode 100644
index 0000000..a54cb79
--- /dev/null
+++ b/src/main/java/com/MTAPizza/Sympoll/groupmanagementservice/repository/GroupRepository.java
@@ -0,0 +1,9 @@
+package com.MTAPizza.Sympoll.groupmanagementservice.repository;
+
+import com.MTAPizza.Sympoll.groupmanagementservice.model.Group;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface GroupRepository extends JpaRepository {
+}
diff --git a/src/main/java/com/MTAPizza/Sympoll/groupmanagementservice/repository/MemberRepository.java b/src/main/java/com/MTAPizza/Sympoll/groupmanagementservice/repository/MemberRepository.java
new file mode 100644
index 0000000..10d5d5b
--- /dev/null
+++ b/src/main/java/com/MTAPizza/Sympoll/groupmanagementservice/repository/MemberRepository.java
@@ -0,0 +1,9 @@
+package com.MTAPizza.Sympoll.groupmanagementservice.repository;
+
+import com.MTAPizza.Sympoll.groupmanagementservice.model.Member;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface MemberRepository extends JpaRepository {
+}
diff --git a/src/main/java/com/MTAPizza/Sympoll/groupmanagementservice/service/GroupService.java b/src/main/java/com/MTAPizza/Sympoll/groupmanagementservice/service/GroupService.java
new file mode 100644
index 0000000..50ae207
--- /dev/null
+++ b/src/main/java/com/MTAPizza/Sympoll/groupmanagementservice/service/GroupService.java
@@ -0,0 +1,33 @@
+package com.MTAPizza.Sympoll.groupmanagementservice.service;
+
+import com.MTAPizza.Sympoll.groupmanagementservice.dto.request.GroupCreateRequest;
+import com.MTAPizza.Sympoll.groupmanagementservice.dto.response.GroupResponse;
+import com.MTAPizza.Sympoll.groupmanagementservice.model.Group;
+import com.MTAPizza.Sympoll.groupmanagementservice.repository.GroupRepository;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class GroupService {
+ private final GroupRepository groupRepository;
+
+ public GroupResponse createGroup(GroupCreateRequest groupCreateRequest) {
+ // TODO: Validate request
+
+ // TODO: Add creator Id into a new list of admins
+ Group createdGroup = Group.builder()
+ .groupId(groupCreateRequest.groupId())
+ .groupName(groupCreateRequest.groupName())
+ .description(groupCreateRequest.description())
+ .creatorId(groupCreateRequest.creatorId())
+ .build();
+
+ groupRepository.save(createdGroup);
+ log.info("Group with ID - '{}' was created by - '{}'", createdGroup.getGroupId(), createdGroup.getCreatorId());
+
+ return createdGroup.toGroupResponse();
+ }
+}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index d4c4cfc..ad34d9c 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1 +1,14 @@
-spring.application.name=group-management-service
+server.port=8085
+spring.application.name=group-service
+spring.datasource.url=jdbc:postgresql://group-db:5435/postgres
+spring.datasource.username=postgres
+spring.datasource.password=1
+spring.datasource.driver-class-name=org.postgresql.Driver
+spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
+
+# Use These Settings to Debug Spring Boot:
+# ---------------------------------------
+# logging.level.root=DEBUG
+# logging.level.org.springframework.web=DEBUG
+# logging.level.org.springframework=DEBUG
+# logging.level.org.hibernate=ERROR
\ No newline at end of file