DeviceArgumentResolver.java

package com.oliwier.listmebackend.identity;

import com.oliwier.listmebackend.domain.model.Device;
import com.oliwier.listmebackend.domain.service.DeviceService;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.server.ResponseStatusException;

import java.util.UUID;

@Component
@RequiredArgsConstructor
public class DeviceArgumentResolver implements HandlerMethodArgumentResolver {

    private static final String DEVICE_HEADER = "X-Device-Id";
    private static final String USER_HEADER = "X-User-Id";

    private final DeviceService deviceService;

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(CurrentDevice.class)
               && Device.class.isAssignableFrom(parameter.getParameterType());
    }

    @Override
    public Device resolveArgument(MethodParameter parameter,
                                  ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest,
                                  WebDataBinderFactory binderFactory) {

        HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
        if (request == null) {
            throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR);
        }

        String rawDevice = request.getHeader(DEVICE_HEADER);
        if (rawDevice == null || rawDevice.isBlank()) {
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
                    "X-Device-Id header is required");
        }

        UUID deviceId;
        try {
            deviceId = UUID.fromString(rawDevice.trim());
        } catch (IllegalArgumentException e) {
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
                    "X-Device-Id must be a valid UUID");
        }

        // Resolve userId: prefer X-User-Id, fall back to X-Device-Id (same UUID used as userId for older clients)
        String rawUser = request.getHeader(USER_HEADER);
        if (rawUser == null || rawUser.isBlank()) rawUser = rawDevice;

        UUID userId;
        try {
            userId = UUID.fromString(rawUser.trim());
        } catch (IllegalArgumentException e) {
            userId = deviceId; // safe fallback
        }

        return deviceService.getOrCreate(deviceId, userId);
    }
}