http 응답 검증 로직 추가
This commit is contained in:
@@ -227,11 +227,24 @@ namespace UVC.Data
|
||||
}
|
||||
|
||||
IDataObject? dataObject = null;
|
||||
result = result.Trim();
|
||||
|
||||
if (!string.IsNullOrEmpty(result))
|
||||
{
|
||||
result = result.Trim();
|
||||
try
|
||||
{
|
||||
string? responseData = info.ResponseMask.Apply(result);
|
||||
// responseData가 null인 경우는 올바른 응답이 아니므로 처리하지 않음
|
||||
if (responseData == null)
|
||||
{
|
||||
throw new Exception($"유효하지 않은 response data. {result}");
|
||||
}
|
||||
else
|
||||
{
|
||||
result = responseData.Trim();
|
||||
}
|
||||
|
||||
|
||||
if (result.StartsWith("{"))
|
||||
{
|
||||
if (info.DataMapper != null)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#nullable enable
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@@ -34,6 +35,7 @@ namespace UVC.Data
|
||||
private int _repeatCount = 0; // 반복 횟수, 0은 무한 반복
|
||||
private int _repeatInterval = 1000; // 반복 간격 (ms)
|
||||
private DataMapper? _dataMapper = null; // 데이터 매퍼
|
||||
private ResponseMask _responseMask = new ResponseMask(); // response 데이터 성공 여부와 데이터를 구분하는 마스크
|
||||
private int _maxRetryCount = 3;
|
||||
private int _retryDelay = 1000; // 밀리초
|
||||
private bool _updatedDataOnly = false; // 업데이트된 데이터만 받을 여부
|
||||
@@ -83,6 +85,11 @@ namespace UVC.Data
|
||||
/// </summary>
|
||||
public DataMapper? DataMapper => _dataMapper;
|
||||
|
||||
/// <summary>
|
||||
/// response에 적용되는 데이터 마스크
|
||||
/// </summary>
|
||||
public ResponseMask ResponseMask => _responseMask;
|
||||
|
||||
/// <summary>
|
||||
/// 최대 재시도 횟수
|
||||
/// </summary>
|
||||
@@ -125,6 +132,17 @@ namespace UVC.Data
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// HTTP 파이프라인에 적용할 response 마스크를 설정하고 업데이트된 파이프라인 구성을 반환합니다.
|
||||
/// </summary>
|
||||
/// <param name="responseMask">HTTP response에 적용할 <see cref="DataMask"/>입니다.</param>
|
||||
/// <returns>지정된 response 마스크가 적용된 업데이트된 <see cref="HttpPipeLineInfo"/> 인스턴스입니다.</returns>
|
||||
public HttpPipeLineInfo setResponseMask(ResponseMask responseMask)
|
||||
{
|
||||
_responseMask = responseMask;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// HTTP 요청이 완료된 후 호출될 핸들러를 설정합니다.
|
||||
/// 변경 된 데이터는 IDataObject로 전달됩니다.
|
||||
@@ -186,4 +204,41 @@ namespace UVC.Data
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class ResponseMask
|
||||
{
|
||||
private string successKey = "message";
|
||||
private string successValue = "success";
|
||||
private string dataKey = "data";
|
||||
|
||||
public string SuccessKey { get; }
|
||||
public string SuccessValue { get; }
|
||||
public string DataKey { get; }
|
||||
|
||||
public ResponseMask() { }
|
||||
|
||||
public ResponseMask(string successKey, string successValue, string dataKey)
|
||||
{
|
||||
this.successKey = successKey;
|
||||
this.successValue = successValue;
|
||||
this.dataKey = dataKey;
|
||||
}
|
||||
|
||||
|
||||
public string? Apply(string response)
|
||||
{
|
||||
JObject responseObject = JObject.Parse(response);
|
||||
if(responseObject.TryGetValue(successKey, out JToken? successToken) &&
|
||||
responseObject.TryGetValue(dataKey, out JToken? dataToken) &&
|
||||
successToken?.ToString().ToLower() == successValue)
|
||||
{
|
||||
// 성공적인 응답 처리 로직
|
||||
return dataToken?.ToString(); // 실제로는 dataKey에 해당하는 데이터만 반환할 수 있습니다.
|
||||
}
|
||||
else
|
||||
{
|
||||
return null; // 실패한 응답 처리 로직
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
using SampleProject.Config;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UVC.Log;
|
||||
using UVC.network;
|
||||
using UVC.Tests;
|
||||
|
||||
@@ -135,30 +137,41 @@ namespace UVC.Data
|
||||
{
|
||||
MQTTPipeLineInfo info = infoList[topic];
|
||||
IDataObject? dataObject = null;
|
||||
|
||||
message = message.Trim();
|
||||
if (!string.IsNullOrEmpty(message))
|
||||
{
|
||||
message = message.Trim();
|
||||
if (message.StartsWith("{"))
|
||||
try
|
||||
{
|
||||
JObject source = JObject.Parse(message);
|
||||
if (info.DataMapper != null) dataObject = info.DataMapper.Map(source);
|
||||
if (message.StartsWith("{"))
|
||||
{
|
||||
JObject source = JObject.Parse(message);
|
||||
if (info.DataMapper != null) dataObject = info.DataMapper.Map(source);
|
||||
}
|
||||
else if (message.StartsWith("["))
|
||||
{
|
||||
JArray source = JArray.Parse(message);
|
||||
if (info.DataMapper != null) dataObject = info.DataMapper.Map(source);
|
||||
}
|
||||
|
||||
if (dataObject != null) dataObject = DataRepository.Instance.AddData(topic, dataObject, info.UpdatedDataOnly);
|
||||
|
||||
// 갱신 된 데이터가 있는 경우 핸들러 호출
|
||||
if (info.UpdatedDataOnly)
|
||||
{
|
||||
if (dataObject != null && dataObject.UpdatedCount > 0) info.Handler?.Invoke(dataObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
info.Handler?.Invoke(dataObject);
|
||||
}
|
||||
}
|
||||
else if (message.StartsWith("["))
|
||||
catch (Exception ex)
|
||||
{
|
||||
JArray source = JArray.Parse(message);
|
||||
if (info.DataMapper != null) dataObject = info.DataMapper.Map(source);
|
||||
// 예외 발생 시 로깅 또는 처리
|
||||
ULog.Error($"Error processing message for topic '{topic}': {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
if (dataObject != null) dataObject = DataRepository.Instance.AddData(topic, dataObject, info.UpdatedDataOnly);
|
||||
// 갱신 된 데이터가 있는 경우 핸들러 호출
|
||||
if (info.UpdatedDataOnly)
|
||||
{
|
||||
if (dataObject != null && dataObject.UpdatedCount > 0) info.Handler?.Invoke(dataObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
info.Handler?.Invoke(dataObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -323,11 +323,13 @@ namespace UVC.network
|
||||
string payload = Encoding.UTF8.GetString(message.Payload.Data, message.Payload.Offset, message.Payload.Count);
|
||||
ULog.Debug($"MQTT OnTopic {topic.Filter.OriginalFilter} => {payload}");
|
||||
ServerLog.LogMqtt(MQTTDomain, MQTTPort.ToString(), topic.Filter.OriginalFilter, payload, DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"));
|
||||
|
||||
if(topicHandler.TryGetValue(topic.Filter.OriginalFilter, out var handler))
|
||||
if(payload.Trim().Length > 0)
|
||||
{
|
||||
// 등록된 핸들러가 있으면 호출
|
||||
handler.Invoke(topic.Filter.OriginalFilter, payload);
|
||||
if (topicHandler.TryGetValue(topic.Filter.OriginalFilter, out var handler))
|
||||
{
|
||||
// 등록된 핸들러가 있으면 호출
|
||||
handler.Invoke(topic.Filter.OriginalFilter, payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -222,7 +222,7 @@ namespace UVC.Tests.Data
|
||||
bool handlerCalled = false;
|
||||
DataObject? receivedData = null;
|
||||
|
||||
var mockResponse = @"{""name"": ""테스트"", ""value"": 123}";
|
||||
var mockResponse = @"{""message"": ""Success"", ""data"": {""name"": ""테스트"", ""value"": 123}}";
|
||||
|
||||
// DataMask와 DataMapper 설정
|
||||
var dataMask = new DataMask();
|
||||
@@ -277,7 +277,7 @@ namespace UVC.Tests.Data
|
||||
bool handlerCalled = false;
|
||||
IDataObject? receivedData = null;
|
||||
|
||||
var mockResponse = @"[{""name"": ""항목1"", ""value"": 10}, {""name"": ""항목2"", ""value"": 20}]";
|
||||
var mockResponse = @"{""message"": ""Success"", ""data"": [{""name"": ""항목1"", ""value"": 10}, {""name"": ""항목2"", ""value"": 20}]}";
|
||||
|
||||
// 배열용 DataMask 설정
|
||||
var dataMask = new DataMask
|
||||
@@ -701,9 +701,9 @@ namespace UVC.Tests.Data
|
||||
// 여러 응답을 순차적으로 반환하기 위한 응답 데이터 설정
|
||||
string[] mockResponses = new string[]
|
||||
{
|
||||
@"{""id"": 1, ""status"": ""pending"", ""timestamp"": ""2025-06-09T10:00:00Z""}",
|
||||
@"{""id"": 1, ""status"": ""processing"", ""timestamp"": ""2025-06-09T10:00:10Z""}",
|
||||
@"{""id"": 1, ""status"": ""completed"", ""timestamp"": ""2025-06-09T10:00:20Z""}"
|
||||
@"{""message"": ""Success"", ""data"": {""id"": 1, ""status"": ""pending"", ""timestamp"": ""2025-06-09T10:00:00Z""}}",
|
||||
@"{""message"": ""Success"", ""data"": {""id"": 1, ""status"": ""processing"", ""timestamp"": ""2025-06-09T10:00:10Z""}}",
|
||||
@"{""message"": ""Success"", ""data"": {""id"": 1, ""status"": ""completed"", ""timestamp"": ""2025-06-09T10:00:20Z""}}"
|
||||
};
|
||||
|
||||
// Mock 응답 설정
|
||||
@@ -780,7 +780,7 @@ namespace UVC.Tests.Data
|
||||
int repeatInterval = 100;
|
||||
|
||||
// Mock 응답 설정
|
||||
string mockResponse = @"{""id"": 2, ""status"": ""running"", ""timestamp"": ""2025-06-09T11:00:00Z""}";
|
||||
string mockResponse = @"{""message"": ""Success"", ""data"": {""id"": 2, ""status"": ""running"", ""timestamp"": ""2025-06-09T11:00:00Z""}}";
|
||||
MockHttpRequester.SetResponse(testUrl, mockResponse);
|
||||
|
||||
// DataMask와 DataMapper 설정
|
||||
@@ -846,8 +846,8 @@ namespace UVC.Tests.Data
|
||||
int repeatInterval2 = 150;
|
||||
|
||||
// Mock 응답 설정
|
||||
string mockResponse1 = @"{""id"": 3, ""name"": ""작업1""}";
|
||||
string mockResponse2 = @"{""id"": 4, ""name"": ""작업2""}";
|
||||
string mockResponse1 = @"{""message"": ""Success"", ""data"": {""id"": 3, ""name"": ""작업1""}}";
|
||||
string mockResponse2 = @"{""message"": ""Success"", ""data"": {""id"": 4, ""name"": ""작업2""}}";
|
||||
|
||||
MockHttpRequester.SetResponse(testUrl1, mockResponse1);
|
||||
MockHttpRequester.SetResponse(testUrl2, mockResponse2);
|
||||
@@ -931,7 +931,7 @@ namespace UVC.Tests.Data
|
||||
int repeatInterval = 100;
|
||||
|
||||
// Mock 응답 설정
|
||||
string mockResponse = @"{""id"": 5, ""message"": ""자동 중단 테스트""}";
|
||||
string mockResponse = @"{""message"": ""Success"", ""data"": {""id"": 5, ""message"": ""자동 중단 테스트""}}";
|
||||
MockHttpRequester.SetResponse(testUrl, mockResponse);
|
||||
|
||||
// DataMask 설정
|
||||
|
||||
@@ -43,14 +43,15 @@ namespace UVC.Tests.Data
|
||||
{
|
||||
Setup();
|
||||
Debug.Log("===== MQTTPipeLine 테스트 시작 =====");
|
||||
// 하나씩 테스트 해야 함
|
||||
//await RunTestAsync(nameof(ExecutePipeLine_AllTopics_RegistersAndHandlesMessages), ExecutePipeLine_AllTopics_RegistersAndHandlesMessages);
|
||||
//await RunTestAsync(nameof(RemoveTopic_ShouldStopReceivingMessages), RemoveTopic_ShouldStopReceivingMessages);
|
||||
//await RunTestAsync(nameof(UpdatedDataOnly_ShouldOnlyCallHandlerForUpdatedData), UpdatedDataOnly_ShouldOnlyCallHandlerForUpdatedData);
|
||||
await RunTestAsync(nameof(UpdatedDataOnly_WithMockMQTTService_ShouldOnlyReceiveUpdatedData), UpdatedDataOnly_WithMockMQTTService_ShouldOnlyReceiveUpdatedData);
|
||||
//await RunTestAsync(nameof(UpdatedDataOnly_WithMockMQTTService_ShouldOnlyReceiveUpdatedData), UpdatedDataOnly_WithMockMQTTService_ShouldOnlyReceiveUpdatedData);
|
||||
//RunTest(nameof(OnTopicMessage_ValidJsonObject_CallsHandler), OnTopicMessage_ValidJsonObject_CallsHandler);
|
||||
//RunTest(nameof(OnTopicMessage_JsonArray_CallsHandler), OnTopicMessage_JsonArray_CallsHandler);
|
||||
//RunTest(nameof(OnTopicMessage_EmptyMessage_DoesNotCallHandler), OnTopicMessage_EmptyMessage_DoesNotCallHandler);
|
||||
//RunTest(nameof(OnTopicMessage_InvalidJson_DoesNotCallHandler), OnTopicMessage_InvalidJson_DoesNotCallHandler);
|
||||
RunTest(nameof(OnTopicMessage_InvalidJson_DoesNotCallHandler), OnTopicMessage_InvalidJson_DoesNotCallHandler);
|
||||
Debug.Log("===== MQTTPipeLine 테스트 완료 =====");
|
||||
}
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ namespace UVC.Tests
|
||||
}
|
||||
else
|
||||
{
|
||||
response = GetResponse(url);
|
||||
response = "{\"message\":\"Success\", \"data\":" + GetResponse(url) + "}";
|
||||
}
|
||||
|
||||
if (typeof(T) == typeof(string))
|
||||
|
||||
@@ -7,8 +7,8 @@ namespace UVC.Tests
|
||||
public static void RunAllTests()
|
||||
{
|
||||
//new DataMapperTests().TestAll();
|
||||
//new HttpPipeLineTests().TestAll();
|
||||
new MQTTPipeLineTests().TestAll();
|
||||
new HttpPipeLineTests().TestAll();
|
||||
//new MQTTPipeLineTests().TestAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user