Skip to content
Merged
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
31 changes: 29 additions & 2 deletions sticker-award/README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,37 @@
# Sticker Award Service

The Sticker Award Service manages the assignment of stickers to users in the Stickerlandia platform. It provides APIs for creating, retrieving, and deleting sticker assignments, with appropriate access controls to ensure users can only manage their own stickers, or the stickers of users to whom they have been granted access.
The Sticker Award Service manages stickers and their assignment to users in the Stickerlandia platform. It provides APIs for sticker catalog management and user sticker assignments.

## Architecture

### Domain Structure
- **`award/`** - User sticker assignment domain
- `StickerAwardResource.java` - HTTP API for user assignments
- `StickerAwardRepository.java` - Data access and entity-DTO mapping
- `dto/` - Request/Response DTOs (AssignStickerRequest, UserAssignmentDTO, etc.)
- `entity/` - Database entities (StickerAssignment)
- `messaging/` - Event publishing

- **`sticker/`** - Sticker catalog domain
- `StickerResource.java` - HTTP API for sticker catalog
- `StickerRepository.java` - Data access and entity-DTO mapping
- `dto/` - Request/Response DTOs (CreateStickerRequest, StickerDTO, etc.)
- `entity/` - Database entities (Sticker)

- **`common/`** - Shared utilities
- `dto/` - Common DTOs (PagedResponse)
- `events/` - Domain events

### Separation of Concerns
- **Resource** - HTTP layer, handles requests/responses, only works with DTOs
- **Repository** - Data layer, maps between entities and DTOs, contains business logic
- **Entity** - Database layer, JPA entities for persistence
- **DTO** - API layer, request/response objects for HTTP APIs

## Features

- Sticker assignment management
- Sticker catalog management (CRUD operations)
- User sticker assignment management
- JWT-based authentication and authorization
- Event-driven integration with other services

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package com.datadoghq.stickerlandia.stickeraward.award;

import com.datadoghq.stickerlandia.stickeraward.award.dto.AssignStickerRequest;
import com.datadoghq.stickerlandia.stickeraward.award.dto.AssignStickerResponse;
import com.datadoghq.stickerlandia.stickeraward.award.dto.StickerAssignmentDTO;
import com.datadoghq.stickerlandia.stickeraward.award.dto.RemoveStickerFromUserResponse;
import com.datadoghq.stickerlandia.stickeraward.award.dto.GetUserStickersResponse;
import com.datadoghq.stickerlandia.stickeraward.sticker.entity.Sticker;
import com.datadoghq.stickerlandia.stickeraward.award.entity.StickerAssignment;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.transaction.Transactional;

import java.time.Instant;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@ApplicationScoped
public class StickerAwardRepository {

public GetUserStickersResponse getUserStickers(String userId) {
List<StickerAssignment> assignments = StickerAssignment.findActiveByUserId(userId);

List<StickerAssignmentDTO> stickerAssignmentDTOS = assignments.stream()
.map(assignment -> {
Sticker sticker = assignment.getSticker();
StickerAssignmentDTO dto = new StickerAssignmentDTO();
dto.setStickerId(sticker.getStickerId());
dto.setName(sticker.getName());
dto.setDescription(sticker.getDescription());
dto.setImageUrl(sticker.getImageUrl());
dto.setAssignedAt(Date.from(assignment.getAssignedAt()));
return dto;
})
.collect(Collectors.toList());

GetUserStickersResponse response = new GetUserStickersResponse();
response.setUserId(userId);
response.setStickers(stickerAssignmentDTOS);

return response;
}

@Transactional
public AssignStickerResponse assignStickerToUser(String userId, String stickerId, AssignStickerRequest request) {
// Find the sticker to assign
Sticker sticker = Sticker.findById(stickerId);
if (sticker == null) {
return null; // Sticker not found
}

// Check if user already has this sticker
StickerAssignment existingAssignment = StickerAssignment.findActiveByUserAndSticker(userId, stickerId);
if (existingAssignment != null) {
throw new IllegalStateException("User already has this sticker assigned");
}

// Create new assignment
StickerAssignment assignment = new StickerAssignment();
assignment.setUserId(userId);
assignment.setSticker(sticker);
assignment.setReason(request.getReason());
assignment.setAssignedAt(Instant.now());
assignment.persist();

// Create response
AssignStickerResponse response = new AssignStickerResponse();
response.setUserId(userId);
response.setStickerId(stickerId);
response.setAssignedAt(Date.from(assignment.getAssignedAt()));

return response;
}

@Transactional
public RemoveStickerFromUserResponse removeStickerFromUser(String userId, String stickerId) {
// Find the active assignment
StickerAssignment assignment = StickerAssignment.findActiveByUserAndSticker(userId, stickerId);
if (assignment == null) {
return null; // Assignment not found
}

// Mark as removed
assignment.setRemovedAt(Instant.now());
assignment.persist();

// Create response
RemoveStickerFromUserResponse response = new RemoveStickerFromUserResponse();
response.setUserId(userId);
response.setStickerId(stickerId);
response.setRemovedAt(Date.from(assignment.getRemovedAt()));

return response;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package com.datadoghq.stickerlandia.stickeraward.award;

import com.datadoghq.stickerlandia.stickeraward.award.dto.AssignStickerRequest;
import com.datadoghq.stickerlandia.stickeraward.award.dto.AssignStickerResponse;
import com.datadoghq.stickerlandia.stickeraward.award.dto.StickerAssignmentDTO;
import com.datadoghq.stickerlandia.stickeraward.award.dto.RemoveStickerFromUserResponse;
import com.datadoghq.stickerlandia.stickeraward.award.dto.GetUserStickersResponse;
import com.datadoghq.stickerlandia.stickeraward.award.messaging.StickerAwardEventPublisher;
import io.smallrye.common.constraint.NotNull;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Response;
import org.eclipse.microprofile.openapi.annotations.Operation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


@Path("/api")
public class StickerAwardResource {

private static final Logger log = LoggerFactory.getLogger(StickerAwardResource.class);

@Inject
StickerAwardEventPublisher eventPublisher;

@Inject
StickerAwardRepository stickerAwardRepository;

@Operation(description = "Get stickers assigned to a user (access controlled based on caller identity)")
@Path("/award/v1/users/{userId}/stickers")
@GET
@Produces("application/json")
public GetUserStickersResponse getUserStickers(@PathParam("userId") String userId) {
return stickerAwardRepository.getUserStickers(userId);
}

@Operation(description = "Assign a new sticker to a user (access controlled based on caller identity)")
@Path("/award/v1/users/{userId}/stickers")
@POST
@Produces("application/json")
@Consumes("application/json")
@Transactional
public Response assignStickerToUser(@PathParam("userId") String userId,
@NotNull AssignStickerRequest data) {

try {
String stickerId = data.getStickerId();
AssignStickerResponse response = stickerAwardRepository.assignStickerToUser(userId, stickerId, data);
if (response == null) {
return Response.status(Response.Status.BAD_REQUEST).build();
}

// Publish events - Note: We'll need to modify event publisher to work with DTOs
// For now, skip event publishing until we refactor the event publisher
// try {
// log.info("Publishing sticker assignment events for userId={}, stickerId={}", userId, stickerId);
// eventPublisher.publishStickerAssigned(...);
// } catch (Exception e) {
// log.error("Failed to publish sticker assignment events", e);
// }

return Response.status(Response.Status.CREATED)
.entity(response)
.build();
} catch (IllegalStateException e) {
return Response.status(Response.Status.CONFLICT).build();
}
}

@Operation(description = "Remove a sticker assignment from a user (access controlled based on caller identity)")
@Path("/award/v1/users/{userId}/stickers/{stickerId}")
@DELETE
@Produces("application/json")
@Transactional
public Response removeStickerFromUser(@PathParam("userId") String userId,
@PathParam("stickerId") String stickerId) {

RemoveStickerFromUserResponse response = stickerAwardRepository.removeStickerFromUser(userId, stickerId);
if (response == null) {
return Response.status(Response.Status.NOT_FOUND).build();
}

// Publish events - Note: We'll need to modify event publisher to work with DTOs
// For now, skip event publishing until we refactor the event publisher
// try {
// log.info("Publishing sticker removal event for userId={}, stickerId={}", userId, stickerId);
// eventPublisher.publishStickerRemoved(...);
// } catch (Exception e) {
// log.error("Failed to publish sticker removal event", e);
// }

return Response.ok(response).build();
}
}
Loading
Loading