Skip to content

Commit 6467560

Browse files
authored
Merge pull request #40 from CSID-DGU/feat/#30-control
#30 [FEAT] IoT 기기 제어 명령 API
2 parents addbedd + 858a810 commit 6467560

File tree

9 files changed

+192
-0
lines changed

9 files changed

+192
-0
lines changed

server/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ dependencies {
2727
implementation 'org.springframework.boot:spring-boot-starter-validation'
2828
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
2929
compileOnly 'org.projectlombok:lombok'
30+
annotationProcessor 'org.projectlombok:lombok'
3031
runtimeOnly 'mysql:mysql-connector-java:8.0.33'
3132
annotationProcessor 'org.projectlombok:lombok'
3233
testImplementation 'org.springframework.boot:spring-boot-starter-test'
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package org.cecd.server.common;
2+
3+
import jakarta.persistence.Column;
4+
import jakarta.persistence.EntityListeners;
5+
import jakarta.persistence.MappedSuperclass;
6+
import lombok.Getter;
7+
import org.springframework.data.annotation.CreatedDate;
8+
import org.springframework.data.annotation.LastModifiedDate;
9+
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
10+
11+
import java.time.LocalDateTime;
12+
13+
@Getter
14+
@EntityListeners(AuditingEntityListener.class)
15+
@MappedSuperclass
16+
public abstract class BaseTimeEntity {
17+
@CreatedDate
18+
@Column(updatable = false)
19+
private LocalDateTime createdAt;
20+
@LastModifiedDate
21+
private LocalDateTime modifiedAt;
22+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package org.cecd.server.controller;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import org.cecd.server.dto.CommandRequest;
5+
import org.cecd.server.service.CommandService;
6+
import org.springframework.http.ResponseEntity;
7+
import org.springframework.web.bind.annotation.*;
8+
9+
@RestController
10+
@RequestMapping
11+
@RequiredArgsConstructor
12+
public class CommandController {
13+
14+
private final CommandService commandService;
15+
16+
@PostMapping("/control")
17+
public ResponseEntity<String> sendControlCommand(@RequestBody CommandRequest commandRequest) {
18+
String result = commandService.forwardToAgent(commandRequest);
19+
return ResponseEntity.ok(result);
20+
}
21+
22+
@PostMapping("/command")
23+
public ResponseEntity<CommandRequest> echoControlCommand(@RequestBody CommandRequest commandRequest) {
24+
return ResponseEntity.ok(commandRequest);
25+
}
26+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package org.cecd.server.domain;
2+
3+
import jakarta.persistence.*;
4+
import lombok.Getter;
5+
import lombok.NoArgsConstructor;
6+
import lombok.Setter;
7+
import org.cecd.server.common.BaseTimeEntity;
8+
9+
@Getter
10+
@Setter
11+
@NoArgsConstructor
12+
@Entity
13+
public class Command extends BaseTimeEntity {
14+
15+
@Id
16+
@GeneratedValue(strategy = GenerationType.IDENTITY)
17+
@Column(name = "command_id")
18+
private Long commandId;
19+
20+
@Column(nullable = false)
21+
private String sensorId;
22+
23+
@Column(nullable = false)
24+
private boolean command; // true : ON, false : OFF
25+
26+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package org.cecd.server.dto;
2+
3+
import lombok.Getter;
4+
import lombok.NonNull;
5+
import lombok.Setter;
6+
7+
@Getter
8+
@Setter
9+
public class CommandRequest {
10+
private String sensorId;
11+
private boolean command;
12+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package org.cecd.server.repository;
2+
3+
import org.cecd.server.domain.Command;
4+
import org.springframework.data.jpa.repository.JpaRepository;
5+
6+
public interface CommandRepository extends JpaRepository<Command, Long> {
7+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package org.cecd.server.service;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import org.cecd.server.dto.CommandRequest;
5+
import org.cecd.server.utils.AgentClient;
6+
import org.springframework.stereotype.Service;
7+
8+
@Service
9+
@RequiredArgsConstructor
10+
public class CommandService {
11+
12+
private final AgentClient agentClient;
13+
14+
public String forwardToAgent(CommandRequest commandRequest) {
15+
// agent로 POST 요청
16+
return agentClient.sendCommand(commandRequest);
17+
}
18+
19+
}
20+
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package org.cecd.server.utils;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import org.cecd.server.dto.CommandRequest;
5+
import org.springframework.http.HttpEntity;
6+
import org.springframework.http.HttpHeaders;
7+
import org.springframework.http.MediaType;
8+
import org.springframework.stereotype.Component;
9+
import org.springframework.web.client.RestTemplate;
10+
11+
@Component
12+
@RequiredArgsConstructor
13+
public class AgentClient {
14+
15+
private final RestTemplate restTemplate = new RestTemplate();
16+
private static final String AGENT_URL = /*agent-server-ip*/ "http://192.168.0.113:3000/control";
17+
18+
public String sendCommand(CommandRequest commandRequest) {
19+
20+
HttpHeaders headers = new HttpHeaders();
21+
headers.setContentType(MediaType.APPLICATION_JSON);
22+
23+
HttpEntity<CommandRequest> request = new HttpEntity<>(commandRequest, headers);
24+
25+
try {
26+
restTemplate.postForEntity(AGENT_URL, request, String.class);
27+
return "Command sent successfully!";
28+
} catch (Exception e) {
29+
return "Failed to send command: " + e.getMessage();
30+
}
31+
}
32+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package org.cecd.server.controller;
2+
3+
import org.cecd.server.dto.CommandRequest;
4+
import org.junit.jupiter.api.Test;
5+
import org.springframework.beans.factory.annotation.Autowired;
6+
import org.springframework.boot.test.context.SpringBootTest;
7+
import org.springframework.boot.test.web.client.TestRestTemplate;
8+
import org.springframework.http.HttpEntity;
9+
import org.springframework.http.HttpHeaders;
10+
import org.springframework.http.HttpMethod;
11+
import org.springframework.http.ResponseEntity;
12+
13+
import static org.assertj.core.api.Assertions.assertThat;
14+
15+
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
16+
class CommandControllerTest {
17+
18+
@Autowired
19+
private TestRestTemplate restTemplate;
20+
21+
@Test
22+
void sendControlCommand() {
23+
24+
// 요청 데이터 생성
25+
CommandRequest commandRequest = new CommandRequest();
26+
commandRequest.setSensorId("000100010000000093");
27+
commandRequest.setCommand(true);
28+
29+
HttpHeaders headers = new HttpHeaders();
30+
headers.add("Content-Type", "application/json");
31+
32+
HttpEntity<CommandRequest> request = new HttpEntity<>(commandRequest, headers);
33+
34+
// 테스트용 엔드포인트로 POST 요청 전송
35+
ResponseEntity<String> response = restTemplate.exchange(
36+
"/command",
37+
HttpMethod.POST,
38+
request,
39+
String.class
40+
);
41+
42+
// 응답 검증
43+
assertThat(response.getStatusCodeValue()).isEqualTo(200); // 상태 코드 검증
44+
assertThat(response.getBody()).contains("Command sent successfully"); // 응답 메시지 검증
45+
}
46+
}

0 commit comments

Comments
 (0)