Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
7ae8102
refactor(linebreak): remove unnecessary breaks
parthokr Dec 12, 2025
9cfdbf7
feat(audit): add lastAccessedAt
parthokr Dec 12, 2025
a538224
feat(col): add lastAccessedAt column to service user table
parthokr Dec 12, 2025
761e220
feat(audit): update last access timestamp
parthokr Dec 12, 2025
c3a6b38
refactor(permission): use label instead of description
parthokr Dec 12, 2025
1d5cdc0
feat(sse-token): implement sse-token obtain system
parthokr Dec 12, 2025
a076b0d
feat(permission): handle permissions for service user
parthokr Dec 12, 2025
0ff84d5
feat(api): add new api findByIdWithPermissionsAndCreator
parthokr Dec 12, 2025
63253cb
feat(api): add new api to get all memberships (no paging)
parthokr Dec 12, 2025
173561f
feat(internal): implement internal rest communication
parthokr Dec 12, 2025
d55ef58
fix(exception): handle InvalidFormatException inside HttpMessageNotRe…
parthokr Dec 12, 2025
6895cbc
feat(api): add api to update member role in an organization
parthokr Dec 12, 2025
ca30a22
fix(dto): include membershipId while building OrganizationMemberRespo…
parthokr Dec 12, 2025
eb3d8cf
fix(notification): send out notification about organization update
parthokr Dec 12, 2025
7c8c6a9
fix(notification): set notification type to INVITATION_SENT
parthokr Dec 12, 2025
e7b4aba
refactor(remove): remove unused code
parthokr Dec 12, 2025
f9417f1
fix(notification): send notification to the inviter on accepting an i…
parthokr Dec 12, 2025
f4b7091
fix(invitation): prevent leaking invitation info if requested user is…
parthokr Dec 12, 2025
89212e8
feat(type): add INVITATION_SENT, INVITATION_ACCEPTED, ORGANIZATION_ME…
parthokr Dec 12, 2025
f02c8d3
fix(slug): remove slug from dto
parthokr Dec 12, 2025
716c6a5
feat(role): add role update system
parthokr Dec 12, 2025
229b44b
fix(field): include membershipId
parthokr Dec 12, 2025
b1aef3c
feat(route): add route to fetch service user by id
parthokr Dec 12, 2025
45f8e00
feat(route): add route to fetch available permissions for service user
parthokr Dec 12, 2025
d5206f8
fix(dto): handle permission while creating/reading a service user
parthokr Dec 12, 2025
86b97ae
feat(permission): add new permission READ_SSE_TOKEN
parthokr Dec 12, 2025
ec10d83
feat(label): introduce permission label
parthokr Dec 12, 2025
bcfd828
feat(description): add meaningful description
parthokr Dec 12, 2025
6e25eed
feat(dto): add dto to handle service user read response
parthokr Dec 12, 2025
803eecd
feat(dto): add dto to handle service user permissions response
parthokr Dec 12, 2025
fb73957
feat(nats): add nats-dev service
parthokr Dec 12, 2025
4e6d62b
feat(nats): add nats service
parthokr Dec 12, 2025
3287973
feat(nats): set nats config
parthokr Dec 12, 2025
f9a32d6
feat(nats): add nats dependency
parthokr Dec 12, 2025
977a966
feat(nats): add configuration and prepare Connection bean
parthokr Dec 12, 2025
5acfe64
feat(nats): add nats publisher
parthokr Dec 12, 2025
f67b04e
feat(nats): publish notification id in nats
parthokr Dec 12, 2025
db388fd
feat(nats): add nats config in test profile
parthokr Dec 12, 2025
416ae00
feat(nats): add nats-dev to depends_on of backend
parthokr Dec 12, 2025
7f60a61
fix(test): remove test
parthokr Dec 12, 2025
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
44 changes: 0 additions & 44 deletions .github/workflows/ci.yml

This file was deleted.

1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ dependencies {
implementation 'org.mapstruct:mapstruct:1.6.3'
implementation "org.projectlombok:lombok-mapstruct-binding:0.2.0"
implementation 'org.flywaydb:flyway-database-postgresql'
implementation 'io.nats:jnats:2.24.1'

compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
Expand Down
10 changes: 10 additions & 0 deletions compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ services:
- "${SERVER_PORT:-9090}:${SERVER_PORT:-9090}"
depends_on:
- db-dev
- nats-dev
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:${SERVER_PORT:-9090}/actuator/health"]
interval: 10s
Expand All @@ -39,6 +40,15 @@ services:
networks:
- heapdog-network-dev

nats-dev:
container_name: nats-dev
image: nats:2.12.2-alpine3.22
ports:
- "4223:4222"
- "8223:8222"
networks:
- heapdog-network-dev

volumes:
db_data_dev:

Expand Down
20 changes: 8 additions & 12 deletions compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,18 +63,14 @@ services:
networks:
- heapdog-network


test:
build:
context: .
dockerfile: Dockerfile.test
working_dir: /home/gradle/app
environment:
SPRING_PROFILES_ACTIVE: test
volumes:
- .:/home/gradle/app
- ./build/reports:/home/gradle/app/build/reports
command: [ "./gradlew", "test", "jacocoTestReport", "--info" ]
nats:
container_name: nats
image: nats:2.12.2-alpine3.22
ports:
- "4222:4222"
- "8222:8222"
networks:
- heapdog-network

volumes:
db_data_prod:
Expand Down
23 changes: 23 additions & 0 deletions src/main/java/io/heapdog/core/config/NatsConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.heapdog.core.config;


import io.nats.client.Connection;
import io.nats.client.Nats;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;

@Configuration
public class NatsConfig {

@Value("${nats.url}")
private String natsUrl;

@Bean(destroyMethod = "close")
public Connection natsConnection() throws IOException, InterruptedException {
return Nats.connect(natsUrl);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.heapdog.core.feature.notification;


import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/internal/notifications")
@RequiredArgsConstructor
public class InternalNotificationController {

private final NotificationService notificationService;

@GetMapping("/{id}")
InternalNotificationResponseDto
getNotifications(@PathVariable Long id) {
return notificationService.getNotificationById(id);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package io.heapdog.core.feature.notification;

import lombok.Builder;
import lombok.Getter;
import lombok.Setter;

import java.time.Instant;


@Getter
@Setter
@Builder
public class InternalNotificationResponseDto {
private Long id;
private String message;
private String link;
private boolean read;
private boolean clicked;
private NotificationType type;
private Instant createdAt;
private Long userId;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;

import java.util.List;
Expand All @@ -26,6 +27,22 @@ public Notification createNotification(String message, String link) {
return notificationRepository.save(notification);
}

@PreAuthorize("hasAuthority('read:notification')")
InternalNotificationResponseDto getNotificationById(Long notificationId) {
Notification notification = notificationRepository.findById(notificationId)
.orElseThrow(() -> new ResourceNotFoundException("Notification", "id", notificationId.toString()));
return InternalNotificationResponseDto.builder()
.id(notification.getId())
.message(notification.getMessage())
.link(notification.getLink())
.read(notification.isRead())
.clicked(notification.isClicked())
.type(notification.getType())
.createdAt(notification.getCreatedAt())
.userId(notification.getRecipient().getId())
.build();
}

Page<NotificationResponseDto>
getNotifications(Long userId, Long page, Long size) {
return notificationRepository.findByRecipientId(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

public enum NotificationType {

INVITATION
INVITATION_SENT,
INVITATION_ACCEPTED,
ORGANIZATION_MEMBER_ROLE_UPDATED, ORGANIZATION_UPDATED,

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

import java.util.List;
import java.util.Optional;

public interface MembershipRepository extends JpaRepository<Membership, Long> {
Expand All @@ -24,7 +25,8 @@ public interface MembershipRepository extends JpaRepository<Membership, Long> {
m.user.id,
m.user.username,
m.user.email,
m.role
m.role,
m.id
)
FROM Membership m
WHERE m.organization.id = :organizationId
Expand All @@ -33,4 +35,11 @@ SELECT COUNT(m) FROM Membership m
WHERE m.organization.id = :organizationId
""")
Page<OrganizationMemberResponseDto> findByOrganizationId(Long organizationId, Pageable pageable);

@Query("""
SELECT m FROM Membership m
JOIN FETCH m.organization
WHERE m.organization.id = :organizationId
""")
List<Membership> findByOrganizationId(Long organizationId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ public class OrganizationBasicInfoUpdateRequestDto {

private String name;

private String slug;

@Size(max = 100, message = "Description can be at most 100 characters")
private String description;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,12 @@ OrganizationInvitationAcceptResponseDto acceptInvitation(@PathVariable String sl
return organizationService.acceptInvitation(slug, dto, user.getId());
}

@PreAuthorize("@organizationSecurity.isAdmin(#slug, authentication)")
@PatchMapping("/{slug}/membership/{membershipId}/role")
OrganizationMemberResponseDto updateOrganizationMemberRole(@PathVariable String slug,
@PathVariable Long membershipId,
@Valid @RequestBody OrganizationMemberRoleUpdateRequestDto dto) {
return organizationService.updateOrganizationMemberRole(slug, membershipId, dto);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ public class OrganizationMemberResponseDto {
private String username;
private String email;
private OrganizationRole role;
private Long membershipId;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.heapdog.core.feature.organization;


import lombok.Builder;
import lombok.Data;

@Builder
@Data
public class OrganizationMemberRoleUpdateRequestDto {

private OrganizationRole role;

}
Loading