Skip to content

Commit

Permalink
Merge pull request #19 from OceanNetworksCanada/issue-11-support-new-…
Browse files Browse the repository at this point in the history
…restart-cancel-and-status-methods-add-tests

issue-11: Implement restart, cancel, and status functions and add tests
  • Loading branch information
IslaL authored Apr 23, 2024
2 parents cd76c88 + 94c9daf commit 3789819
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 40 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/matlab.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,5 @@ jobs:
source-folder: 'onc'
env:
TOKEN: ${{ secrets.TOKEN }}
ONC_ENV: true
ONC_ENV: 'prod'

83 changes: 72 additions & 11 deletions onc/+onc/OncDelivery.m
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,9 @@
fprintf('Requesting data product...\n');
[r, info] = this.doRequest(url, filters);
status = info.status;
if not(util.is_failed_response(r, status))
this.estimatePollPeriod(r);
this.printProductRequest(r);
else
throw(util.prepare_exception(status));
end
this.estimatePollPeriod(r);
this.printProductRequest(r);

end

function [r, status] = runDataProduct(this, dpRequestId, waitComplete)
Expand All @@ -106,16 +103,18 @@

% run timed run request
tic
cancelUrl = url + "?method=cancel&token="+string(this.token)+"&dpRequestId=" + string(dpRequestId);
if waitComplete
fprintf('\nTo cancel this data product, visit url:\n %s\n', cancelUrl);
else
fprintf('\nTo cancel this data product, please execute command ''onc.cancelDataProduct(%d)''\n', dpRequestId)
end
flag = 'queued';
while ~strcmp(flag,'complete') && ~strcmp(flag,'cancelled')
[response, info] = this.doRequest(url, filters);
status = info.status;
r.requestCount = r.requestCount + 1;

% guard against failed request
if util.is_failed_response(response, status)
throw(util.prepare_exception(status));
end
% repeat only if waitComplete
if waitComplete
log.printResponse(response);
Expand All @@ -140,6 +139,42 @@
r.runIds = [r.runIds, run.dpRunId];
end
end

function response = checkDataProduct(this, dpRequestId)
%% Check the status of a data product
%
%
% checkDataProduct (dpRequestId)
%
% * dpRequestId (int) Request id obtained by requestDataProduct()
%
% Returns: response (struct): status of this data product
%
url = sprintf('%sapi/dataProductDelivery', this.baseUrl);
filters = struct('method', 'status', 'token', this.token, 'dpRequestId', dpRequestId);
response = this.doRequest(url, filters);
end

function [response, info] = cancelDataProduct(this, dpRequestId)
%% Cancel a running data product
%
%
% cancelDataProduct (dpRequestId)
%
% * dpRequestId (int) Request id obtained by requestDataProduct()
%
% Returns: response (struct): cancel process status and message
% info (struct): cancel process http code and status
%
url = sprintf('%sapi/dataProductDelivery', this.baseUrl);
filters = struct('method', 'cancel', 'token', this.token, 'dpRequestId', dpRequestId);
[response, info] = this.doRequest(url, filters);
if isfield(response, 'status') && strcmp(response.status, 'cancelled') && info.status == 200
fprintf("The data product with request id %d and run id %d has been successfully cancelled.\n", dpRequestId, response.dpRunId);
else
fprintf("Failed to cancel the data Product.\n");
end
end

function fileData = downloadDataProduct(this, runId, varargin)
%% Download a data product manually with a runId
Expand Down Expand Up @@ -169,8 +204,34 @@
fileData = this.downloadProductFiles(runId, metadata, maxRetries, overwrite);
end
end

function [response, info] = restartDataProduct(this, dpRequestId, waitComplete)
%% Restart a cancelled data product
%
%
% restartDataProduct (dpRequestId, waitComplete)
%
% * dpRequestId (int) Request id obtained by requestDataProduct()
% - waitComplete (optional): wait until dp finish when set to true (default)
%
% Returns: response (struct): restart process status and message
% info (struct): restart process http code and status
%
if ~exist('waitComplete','var'), waitComplete = true; end
url = sprintf('%sapi/dataProductDelivery', this.baseUrl);
filters = struct('method', 'restart', 'token', this.token, 'dpRequestId', dpRequestId);
[response, info] = this.doRequest(url, filters);
if isfield(response, 'status') && (strcmp(response.status, 'data product running') || strcmp(response.status, 'queued')) && info.status == 200
fprintf("The data product with request id %d and run id %d has been successfully restarted.\n", dpRequestId, response.dpRunId);
else
fprintf("Failed to restart the data product.\n");
end
if waitComplete
[response, info] = this.runDataProduct(dpRequestId, true);
end
end
end

methods (Access = private, Hidden = true)
function fileList = downloadProductFiles(this, runId, varargin)
%% Download all data product files for provided run id
Expand Down
9 changes: 4 additions & 5 deletions onc/+util/is_failed_response.m
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,22 @@
%% Checks if a server response describes a failure
%
% * response: (struct) Response as returned by do_request()
% - status: @TODO
% - status: (double) http status code
%
% Returns: (Logical) true when the response is a failure
isFailed = false;

% Fail if HTTP status code is not a 2xx
if exist('status', 'var')
if status < 200 || status > 226
isFailed = true;
return
end
end

% Fail if the response is an error description
if isfield(response, "errors")
names = fieldnames(response.errors(1));
isFailed = ismember("errorCode", names) && ismember("errorMessage", names);
end
end

end
10 changes: 8 additions & 2 deletions onc/tests/globals.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
cd(fileparts(which(mfilename)));
end

% grab token from "TOKEN" file
% grab token from "TOKEN" file or get from env
f = fopen('TOKEN','r');
if f > 0
token = fgetl(f);
Expand All @@ -20,8 +20,14 @@
token = getenv('TOKEN');
end

% get environment from ONC_ENV or use QA as default
config.production = getenv('ONC_ENV');
if strcmp(config.production, 'prod')
config.production = true;
else
config.production = false;
end
% Set and save config
config.production = true;
config.showInfo = false;
config.outPath = 'output';
config.timeout = 60;
Expand Down
62 changes: 53 additions & 9 deletions onc/tests/suites/Test07_DataProductDelivery.m
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,40 @@ function testValidResultsOnly(this)


end

%% Testing run method
function testInvalidRequestId(this)
verifyError(this, @() this.onc.runDataProduct(1234567890), 'onc:http400:error127');
end

%% Testing download method
function testInvalidRunId(this)
verifyError(this, @() this.onc.downloadDataProduct(1234567890), 'onc:http400:error127');
end

%% Testing cancel method
function testCancelWithInvalidRequestId(this)
verifyError(this, @() this.onc.cancelDataProduct(1234567890), 'onc:http400:error127');
end

%% Testing status method
function testStatusWithInvalidRequestId(this)
verifyError(this, @() this.onc.checkDataProduct(1234567890), 'onc:http400:error127');
end

%% Testing restart method
function testRestartWithInvalidRequestId(this)
verifyError(this, @() this.onc.restartDataProduct(1234567890), 'onc:http400:error127');
end

%% Integration tests
function testValidManual(this)
this.updateOncOutPath('output/testValidManual');
requestId = this.onc.requestDataProduct(this.Params).dpRequestId;
statusBeforeDownload = this.onc.checkDataProduct(requestId);

assertEqual(this, statusBeforeDownload.searchHdrStatus, 'OPEN');

runId = this.onc.runDataProduct(requestId).runIds(1);
data = this.onc.downloadDataProduct(runId);
verifyTrue(this, length(data) == 3, ...
Expand All @@ -133,16 +163,30 @@ function testValidManual(this)
verify_files_in_path(this, this.onc.outPath, 3);
verify_field_value_type(this, data(1), this.expectedFields);

statusAfterDownload = this.onc.checkDataProduct(requestId);
assertEqual(this, statusAfterDownload.searchHdrStatus, 'COMPLETED');
end

%% Testing run method
function testInvalidRequestId(this)
verifyError(this, @() this.onc.runDataProduct(1234567890), 'onc:http400:error127');
end

%% Testing download method
function testInvalidRunId(this)
verifyError(this, @() this.onc.downloadDataProduct(1234567890), 'onc:http400:error127');

function testValidCancelRestart(this)
this.updateOncOutPath('output/testValidCancelRestart');
requestId = this.onc.requestDataProduct(this.Params).dpRequestId;
runId = this.onc.runDataProduct(requestId, false).runIds(1);
responseCancel = this.onc.cancelDataProduct(requestId);

verify_field_value(this, responseCancel, 'dpRunId', runId);
verify_field_value(this, responseCancel, 'status', 'cancelled');

%update MATLAB:nonExistentField error to actual http400 error for this test
%after api service fixes the issue that this 400 error does not contain "errors" field
assertError(this, @() this.onc.downloadDataProduct(runId), 'MATLAB:nonExistentField')

runIdAfterRestart = this.onc.restartDataProduct(requestId).runIds(1);
assertEqual(this, runIdAfterRestart, runId);

responseDownload = this.onc.downloadDataProduct(runId);
assertEqual(this, length(responseDownload), 3, "The first two are png files, and the third one is the metadata");
verify_files_in_path(this, this.onc.outPath, 3);
verify_field_value_type(this, responseDownload(1), this.expectedFields);
end
end

Expand Down
25 changes: 13 additions & 12 deletions onc/tests/suites/Test08_RealTime.m
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ function testDeviceValidParamsOnePage(this)
result = this.onc.getDirectByDevice(this.paramsDevice);
resultAllPages = this.onc.getDirectByDevice(this.paramsDeviceMultiPages, 'allPages', true);
assertTrue(this, length(result.sensorData(1).data.values) > this.paramsDeviceMultiPages.rowLimit, ...
'Test should return at least `rowLimit` rows for each sensor.');
'Test should return at least `rowLimit` rows.');
assertEmpty(this, result.next, 'Test should return only one page.');
assertEqual(this, resultAllPages.sensorData(1).data, result.sensorData(1).data, ...
'Test should concatenate rows for all pages.');
Expand All @@ -178,8 +178,7 @@ function testDeviceValidParamsMultiplePages(this)
end

%% Testing rawdata device
%{
% waiting for python updates

function testRawDeviceInvalidParamValue(this)
filters = this.paramsDevice;
filters.deviceCode = 'XYZ123';
Expand All @@ -196,27 +195,29 @@ function testRawDeviceNoData(this)
filters = this.paramsDevice;
filters.dateFrom = '2000-01-01';
filters.dateTo = '2000-01-02';
result = this.onc.getDirectByDevice(filters);
assertEmpty(this, result.sensorData);
result = this.onc.getDirectRawByDevice(filters);
assertEmpty(this, result.data.lineTypes);
assertEmpty(this, result.data.readings);
assertEmpty(this, result.data.times);
end

function testRawDeviceValidParamsOnePage(this)
result = this.onc.getDirectByDevice(this.paramsDevice);
resultAllPages = this.onc.getDirectByDevice(this.paramsDeviceMultiPages, 'allPages', true);
assertTrue(this, length(result.sensorData(1).data.values) > this.paramsDeviceMultiPages.rowLimit, ...
result = this.onc.getDirectRawByDevice(this.paramsDevice);
resultAllPages = this.onc.getDirectRawByDevice(this.paramsDeviceMultiPages, 'allPages', true);
assertTrue(this, length(result.data.readings) > this.paramsDeviceMultiPages.rowLimit, ...
'Test should return at least `rowLimit` rows for each sensor.');
assertEmpty(this, result.next, 'Test should return only one page.');
assertEqual(this, resultAllPages.sensorData(1).data, result.sensorData(1).data, ...
assertEqual(this, resultAllPages.data, result.data, ...
'Test should concatenate rows for all pages.');
assertEmpty(this, resultAllPages.next, 'Test should return only one page.');
end

function testRawDeviceValidParamsMultiplePages(this)
result = this.onc.getDirectByDevice(this.paramsDeviceMultiPages);
assertEqual(this, length(result.sensorData(1).data.values), this.paramsDeviceMultiPages.rowLimit, ...
result = this.onc.getDirectRawByDevice(this.paramsDeviceMultiPages);
assertEqual(this, length(result.data.readings), this.paramsDeviceMultiPages.rowLimit, ...
'Test should only return `rowLimit` rows for each sensor.');
assertTrue(this, ~isempty(result.next), 'Test should return multiple pages.');
end
%}

end
end

0 comments on commit 3789819

Please sign in to comment.