File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
package kr.itn.itnhub.mattermost;
import com.fasterxml.jackson.databind.JsonNode;
import kr.itn.itnhub.config.MattermostProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestClientException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
@Component
public class MattermostRestClient implements MattermostClient {
private final RestClient rest;
private final String teamId;
@Autowired
public MattermostRestClient(MattermostProperties props) {
this(props, RestClient.builder());
}
/**
* 전송 계층(transport)을 직접 지정할 수 있는 생성자. 운영 코드에서는 사용하지 않으며,
* 테스트 등 별도의 {@link RestClient.Builder} 설정이 필요한 호출자를 위한 확장 지점이다.
*/
MattermostRestClient(MattermostProperties props, RestClient.Builder builder) {
this.teamId = props.teamId();
this.rest = builder
.baseUrl(props.baseUrl())
.defaultHeader("Authorization", "Bearer " + props.token())
.build();
}
@Override
public Optional<String> findChannelIdByInternalName(String internalName) {
try {
JsonNode body = rest.get()
.uri("/api/v4/teams/{teamId}/channels/name/{name}", teamId, internalName)
.retrieve()
.onStatus(status -> status.value() == 404, (req, res) -> {
throw new NotFound();
})
.body(JsonNode.class);
return idOf(body);
} catch (NotFound e) {
return Optional.empty();
} catch (RestClientException e) {
throw new MattermostException("채널 조회 실패: " + internalName, e);
}
}
@Override
public Optional<String> findChannelIdByDisplayName(String displayName) {
try {
JsonNode channels = rest.get()
.uri(uri -> uri.path("/api/v4/users/me/teams/{teamId}/channels")
.build(teamId))
.retrieve()
.body(JsonNode.class);
if (channels == null || !channels.isArray()) {
return Optional.empty();
}
for (JsonNode channel : channels) {
if (displayName.equals(channel.path("display_name").asText())) {
return idOf(channel);
}
}
return Optional.empty();
} catch (RestClientException e) {
throw new MattermostException("채널 목록 조회 실패", e);
}
}
@Override
public String createPrivateChannel(String internalName, String displayName) {
Map<String, String> body = new LinkedHashMap<>();
body.put("team_id", teamId);
body.put("name", internalName);
body.put("display_name", displayName);
body.put("type", "P");
body.put("purpose", "");
body.put("header", "");
try {
JsonNode created = rest.post()
.uri("/api/v4/channels")
.contentType(MediaType.APPLICATION_JSON)
.body(body)
.retrieve()
.body(JsonNode.class);
return idOf(created).orElseThrow(() ->
new MattermostException("채널 생성 응답에 id가 없습니다: " + displayName));
} catch (RestClientException e) {
throw new MattermostException("채널 생성 실패: " + displayName, e);
}
}
private Optional<String> idOf(JsonNode node) {
if (node == null) {
return Optional.empty();
}
String id = node.path("id").asText("");
return id.isBlank() ? Optional.empty() : Optional.of(id);
}
/** 404를 정상 흐름으로 되돌리기 위한 내부 신호. 밖으로 새지 않는다. */
private static final class NotFound extends RuntimeException {
}
}