Skip to content
This repository has been archived by the owner on Jan 28, 2025. It is now read-only.

feat/135/obfuscation #152

Open
wants to merge 33 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
b1b3852
update launch setting
AndrewThien Nov 15, 2024
6812dc2
test result return
AndrewThien Nov 15, 2024
4ecbcd5
Add basis base files for Obfuscation
AndrewThien Nov 15, 2024
1dd9cc8
Merge branch 'main' into feat/135/obfuscation
AndrewThien Nov 15, 2024
2f83b75
Register Obfuscation service
AndrewThien Nov 15, 2024
d92b54f
added functions to implement low number suppression and rounding
anwarfg Nov 15, 2024
be07a98
config the obfuscation function and value
AndrewThien Nov 15, 2024
ba4ed24
Changes the count in jobResult.Results.Count for obfuscation purposes…
anwarfg Nov 15, 2024
c9e65c5
Merge branch 'main' into feat/135/obfuscation
AndrewThien Nov 18, 2024
e2d0a80
cleaning
AndrewThien Nov 18, 2024
4e49afd
write the docs for new obfuscation functions
AndrewThien Nov 18, 2024
8fa4f87
apply the obfuscation functions
AndrewThien Nov 18, 2024
33f7eeb
update settings
AndrewThien Nov 18, 2024
c378fcd
add comment
AndrewThien Nov 18, 2024
40055fd
Added docstrings to the obfuscation functions.
anwarfg Nov 18, 2024
c2f39c1
add obfuscation options to compose file
AndrewThien Nov 18, 2024
981e9bf
Merge remote-tracking branch 'origin/feat/135/obfuscation' into feat/…
AndrewThien Nov 18, 2024
b9c497c
clean up
AndrewThien Nov 18, 2024
84720e0
Merge branch 'main' into feat/135/obfuscation
AndrewThien Nov 18, 2024
dd06b72
clean up
AndrewThien Nov 18, 2024
4c2f674
Changed error handling of SubmitResults, so it no longer goes into an…
anwarfg Nov 19, 2024
9a75980
Moved ObfuscationOptions into ObfuscationService.
anwarfg Nov 21, 2024
e6ea2ae
Moved obfuscation function call into AggregateResults after aggregati…
anwarfg Nov 22, 2024
8569e11
Moved obfuscation function call into AggregateResults after aggregati…
anwarfg Nov 22, 2024
493fcd5
Merge remote-tracking branch 'origin/feat/135/obfuscation' into feat/…
anwarfg Nov 25, 2024
63b3c96
Merge remote-tracking branch 'origin/feat/135/obfuscation' into feat/…
anwarfg Nov 25, 2024
1b9e84c
Merge remote-tracking branch 'origin/feat/135/obfuscation' into feat/…
anwarfg Nov 25, 2024
f45b321
Added unit tests for obfuscation.
anwarfg Nov 28, 2024
4345904
Results service now only attempts a retry on internal server error, b…
anwarfg Nov 28, 2024
b61957a
Results service doesn't retry on most error statuses.
anwarfg Nov 28, 2024
ac2c6f5
Merge remote-tracking branch 'origin/feat/135/obfuscation' into feat/…
anwarfg Nov 28, 2024
670ccb2
Merge branch 'main' into feat/135/obfuscation
anwarfg Nov 28, 2024
0f876ba
Clean up after merge conflict.
anwarfg Nov 28, 2024
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
7 changes: 0 additions & 7 deletions app/Hutch.Relay/Config/ObfuscationOptions.cs

This file was deleted.

14 changes: 8 additions & 6 deletions app/Hutch.Relay/Controllers/TaskController.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
using Microsoft.AspNetCore.Authorization;
using System.Reflection;
using System.Text.Json;
using Hutch.Rackit;
using Hutch.Relay.Config;
using Hutch.Relay.Services;
using Hutch.Relay.Services.Contracts;
using Microsoft.Extensions.Options;

namespace Hutch.Relay.Controllers;

Expand All @@ -11,13 +14,14 @@ namespace Hutch.Relay.Controllers;

[ApiController]
[Route("/[controller]")]
[Authorize]
public class TaskController(
IRelayTaskService relayTaskService,
ResultsService resultsService,
IRelayTaskQueue queues,
IObfuscationService obfuscationService) : ControllerBase
IOptions<ApiClientOptions> apiClientOptions) : ControllerBase

{
private ApiClientOptions apiClientOptions = apiClientOptions.Value;

[HttpGet("nextjob/{collectionId}")]
[SwaggerOperation("Fetch next job from queue.")]
Expand Down Expand Up @@ -68,9 +72,7 @@ public async Task<IActionResult> Result(Guid uuid, string collectionId, [FromBod
{
//Aggregate SubTasks Result.Count
var finalResult = await resultsService.AggregateResults(subtask.RelayTask.Id);
// Obfuscate the result
finalResult.Results.Count = obfuscationService.Obfuscate(finalResult.Results.Count);
// Submit the results to TaskApi

await resultsService.SubmitResults(subtask.RelayTask, finalResult);
// Set Task as Complete
await relayTaskService.SetComplete(subtask.RelayTask.Id);
Expand Down
26 changes: 5 additions & 21 deletions app/Hutch.Relay/Services/Contracts/IObfuscationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,10 @@ namespace Hutch.Relay.Services.Contracts;

public interface IObfuscationService
{

/// <summary>
/// Suppresses numbers that are below a specified threshold.
/// </summary>
/// <param name="value">The value to be checked and suppressed.</param>
/// <returns>The original value if it is above the threshold, otherwise 0.</returns>
int LowNumberSuppression(int value);

/// <summary>
/// Rounds a value to the nearest specified number.
/// </summary>
/// <param name="value">The value to be rounded.</param>
/// <returns>The value rounded to the nearest specified number.</returns>
int Rounding(int value);


/// <summary>
/// Applies obfuscation functions to the value based on the obfuscation options.
/// </summary>
/// <param name="value">The value to be obfuscated.</param>
/// <returns>The obfuscated value.</returns>
/// <summary>
/// Applies obfuscation to the value.
/// </summary>
/// <param name="value">Value ot be obfuscated.</param>
/// <returns>The obfuscated value.</returns>
int Obfuscate(int value);
}
49 changes: 26 additions & 23 deletions app/Hutch.Relay/Services/ObfuscationService.cs
Original file line number Diff line number Diff line change
@@ -1,55 +1,58 @@
using Hutch.Relay.Config;
using Hutch.Relay.Services.Contracts;
using Hutch.Relay.Config;
using Microsoft.Extensions.Options;

namespace Hutch.Relay.Services;

public class ObfuscationService: IObfuscationService
public class ObfuscationService(IOptions<ObfuscationOptions> obfuscationOptions): IObfuscationService
{
private readonly ObfuscationOptions _obfuscationOptions;

public ObfuscationService(IOptions<ObfuscationOptions> obfuscationOptions)
{
_obfuscationOptions = obfuscationOptions.Value;
}

/// <summary>
/// Applies low number suppression to the value. If the value is greater than the threshold, it will remain the same. Otherwise, it will be set to 0.
/// Applies low number suppression to the value. If value is greater than the threshold, it will remain the same. Otherwise, it will be set to 0.
/// </summary>
/// <param name="value">The value to apply suppression to.</param>
/// <param name="threshold">The threshold value of suppression.</param>
/// <returns>The suppressed value.</returns>
public int LowNumberSuppression(int value)
private int LowNumberSuppression(int value, int threshold)
{
return value > _obfuscationOptions.LowNumberSuppressionThreshold ? value : 0;
return value > threshold ? value : 0;
}

/// <summary>
/// Applies rounding to the value. The value will be rounded to the nearest specified number.
/// Applies rounding to the value. The value will be rounded to nearest. For example, if nearest = 10, 95 would be rounded to 100, as would 104 and any integer in between.
/// </summary>
/// <param name="value">The value to be rounded.</param>
/// <param name="nearest">The number to round to.</param>
/// <returns>The rounded value.</returns>
public int Rounding(int value)
private int Rounding(int value, int nearest)
{
return _obfuscationOptions.RoundingTarget * (int)Math.Round((float)value / _obfuscationOptions.RoundingTarget);
return nearest * (int)Math.Round((float)value / nearest);
}

/// <summary>
/// Applies obfuscation functions to the value based on the obfuscation options.
/// Applies obfuscation functions to the value. Applies low number suppression first, then rounding. Nearest and threshold are optional; Without them, the function will check the obfuscation options variables, and use those. If they are not present, the obfuscation will not take place for the missing parameter.
/// </summary>
/// <param name="value">The value to be obfuscated.</param>
/// <returns>The obfuscated value.</returns>
/// <param name="value">Value ot be obfuscated.</param>
/// <returns>Returns the obfuscated value.</returns>
public int Obfuscate(int value)
{
if (_obfuscationOptions.LowNumberSuppressionThreshold > 0)

var result = value;
if (obfuscationOptions.Value.LowNumberSuppressionThreshold > 0)
{
value = LowNumberSuppression(value);
result = LowNumberSuppression(result, obfuscationOptions.Value.LowNumberSuppressionThreshold);
}

if (_obfuscationOptions.RoundingTarget > 0)
if (obfuscationOptions.Value.RoundingTarget > 0)
{
value = Rounding(value);
result = Rounding(result, obfuscationOptions.Value.RoundingTarget);
}

return value;
return result;
}
}

public class ObfuscationOptions
{
public int LowNumberSuppressionThreshold { get; set; }
public int RoundingTarget { get; set; }
}
2 changes: 1 addition & 1 deletion app/Hutch.Relay/Services/RelayTaskService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ public async Task<RelaySubTaskModel> GetSubTask(Guid id)
.Include(relaySubTask => relaySubTask.RelayTask)
.SingleOrDefaultAsync(t => t.Id == id)
?? throw new KeyNotFoundException();

return new RelaySubTaskModel()
{
Id = entity.Id,
Expand Down
34 changes: 25 additions & 9 deletions app/Hutch.Relay/Services/ResultsService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ public class ResultsService(
IOptions<ApiClientOptions> options,
ITaskApiClient upstreamTasks,
IRelayTaskService relayTaskService,
IObfuscationService obfuscationService)
IObfuscationService obfuscation
)
{
private ApiClientOptions options = options.Value;

Expand All @@ -36,16 +37,30 @@ public async Task SubmitResults(RelayTaskModel relayTask, JobResult jobResult
}
catch (RackitApiClientException exception)
{
if (exception.UpstreamApiResponse is { StatusCode: HttpStatusCode.InternalServerError })
if (exception.UpstreamApiResponse == null)
{
retryCount++;
logger.LogError(
"Task submission failed with 500 Internal Server Error. Retrying in {delayInSeconds} seconds... ({retryCount}/{maxRetries})",
delayInSeconds,
retryCount, maxRetryCount);
"Task submission failed with unidentified exception. UpstreamApiResponse is null.");
break;
}

HttpStatusCode status = exception.UpstreamApiResponse.StatusCode;

if (status == HttpStatusCode.InternalServerError)
{
retryCount++;
logger.LogError(
$"Task submission failed with {(int)status} {status} Error. Retrying in {delayInSeconds} seconds... ({retryCount}/{maxRetryCount})");
await Task.Delay(delayInSeconds * 1000);
}
else
{
logger.LogError(
$"Task submission failed with {(int)status} {status} Error.");
break;
}


}
}
}
Expand All @@ -64,14 +79,17 @@ public async Task<JobResult> AggregateResults(string relayTaskId)
}
}

// Apply Obfuscation functions if configured
var obfuscatedAggregateCount = obfuscation.Obfuscate(aggregateCount);

return new JobResult()
{
Uuid = relayTaskId,
CollectionId = options.CollectionId ??
throw new ArgumentException(nameof(options.CollectionId)),
Results = new QueryResult()
{
Count = aggregateCount,
Count = obfuscatedAggregateCount,
}
};
}
Expand All @@ -87,8 +105,6 @@ public async Task HandleResultsToExpire()
{
// Aggregate SubTask count
var finalResult = await AggregateResults(task.Id);
// Obfuscate the result
finalResult.Results.Count = obfuscationService.Obfuscate(finalResult.Results.Count);
// Post to TaskApi
await SubmitResults(task, finalResult);
//Set task as complete
Expand Down
2 changes: 1 addition & 1 deletion app/Hutch.Relay/Startup/Web/ConfigureWebService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public static WebApplicationBuilder ConfigureServices(this WebApplicationBuilder

// Obfuscation
builder.Services
.Configure<ObfuscationOptions>(builder.Configuration.GetSection(("Obfuscation")))
.Configure<ObfuscationOptions>(builder.Configuration.GetSection(("ObfuscationOptions")))
.AddTransient<IObfuscationService, ObfuscationService>();

// Hosted Services
Expand Down
11 changes: 11 additions & 0 deletions app/Hutch.Relay/appsettings.Development.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,16 @@
},
"Database": {
"ApplyMigrationsOnStartup": false
},
"ObfuscationOptions": {
"LowNumberSuppressionThreshold": 5,
"RoundingTarget": 10
},
"UpstreamTaskApi": {
"BaseUrl": "https://my-task-api.com",
"CollectionId": "dd52026b-5a61-4c89-b733-04ba141b3f31",
"Username": "user",
"Password": "password"
}

}
59 changes: 59 additions & 0 deletions tests/Hutch.Relay.Tests/Services/ObfuscationServiceTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using Hutch.Relay.Services;
using Microsoft.Extensions.Options;

using Xunit;
using Assert = Xunit.Assert;

namespace Hutch.Relay.Tests.Services;

public class ObfuscationServiceTests
{
// create obfuscation object
[Theory]
// test both rounding and suppression (something to be suppressed, rounding won't matter)
[InlineData(50, 10, 27, 0)]

// test both rounding and suppression (something to be rounded, not suppressed).
[InlineData(10, 10, 2718, 2720)]

//test suppression without rounding.
[InlineData(20, 0, 2718, 2718)]
// test negative number rounding target
[InlineData(20, -1, 2718, 2718)]
// test threshold equality
[InlineData(2717, 0, 2718, 2718)]
[InlineData(2718, 0, 2718, 0)]

// test obfuscation on something to be rounded up/down
[InlineData(0, 10, 2718, 2720)]
[InlineData(0, 10, 271, 270)]
//test negative number threshold target
[InlineData(-1, 10, 271, 270)]
// test neither rounding nor lns
[InlineData(0, 0, 2718, 2718)]

// testing different values for thresh, round and value. just used random numbers to generate these cases
[InlineData(74, 19, 100, 95)]
[InlineData(39, 72, 94, 72)]
[InlineData(29, 50, 13, 0)]
[InlineData(7, 13, 8, 13)]
[InlineData(84, 75, 24, 0)]
[InlineData(86, 48, 74, 0)]

public void Obfuscate_ReturnsCorrectResult(int lnsThresh, int round, int value, int expected)
{
var options = Options.Create(new ObfuscationOptions()
{
LowNumberSuppressionThreshold = lnsThresh,
RoundingTarget = round
}
);

var service = new ObfuscationService(options);

var result = service.Obfuscate(value);

Assert.Equal(expected, result);
}

}